zephyr/drivers/can/can_mcan.c
Henrik Brix Andersen 5e74f72220 drivers: can: mcan: acknowledge all received frames
The Bosch M_CAN IP does not support RX filtering of the RTR bit, so the
driver handles this bit in software.

If a recevied frame matches a filter with RTR enabled, the RTR bit of the
frame must match that of the filter in order to be passed to the RX
callback function. If the RTR bits do not match the frame must be dropped.

Improve the readability of the the logic for determining if a frame should
be dropped and add a missing FIFO acknowledge write for dropped frames.

Fixes: #47204

Signed-off-by: Henrik Brix Andersen <hebad@vestas.com>
2022-07-18 13:24:54 +00:00

1089 lines
29 KiB
C

/*
* Copyright (c) 2022 Vestas Wind Systems A/S
* Copyright (c) 2020 Alexander Wachter
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/sys/util.h>
#include <string.h>
#include <zephyr/cache.h>
#include <zephyr/kernel.h>
#include <zephyr/drivers/can.h>
#include <zephyr/drivers/can/transceiver.h>
#include <zephyr/logging/log.h>
#include "can_mcan.h"
#include "can_mcan_priv.h"
LOG_MODULE_REGISTER(can_mcan, CONFIG_CAN_LOG_LEVEL);
#define CAN_INIT_TIMEOUT (100)
#define CAN_DIV_CEIL(val, div) (((val) + (div) - 1) / (div))
#ifdef CONFIG_CAN_FD_MODE
#define MCAN_MAX_DLC CANFD_MAX_DLC
#else
#define MCAN_MAX_DLC CAN_MAX_DLC
#endif
static void memcpy32_volatile(volatile void *dst_, const volatile void *src_,
size_t len)
{
volatile uint32_t *dst = dst_;
const volatile uint32_t *src = src_;
__ASSERT(len % 4 == 0, "len must be a multiple of 4!");
len /= sizeof(uint32_t);
while (len--) {
*dst = *src;
++dst;
++src;
}
}
static void memset32_volatile(volatile void *dst_, uint32_t val, size_t len)
{
volatile uint32_t *dst = dst_;
__ASSERT(len % 4 == 0, "len must be a multiple of 4!");
len /= sizeof(uint32_t);
while (len--) {
*dst++ = val;
}
}
static int can_exit_sleep_mode(struct can_mcan_reg *can)
{
uint32_t start_time;
can->cccr &= ~CAN_MCAN_CCCR_CSR;
start_time = k_cycle_get_32();
while ((can->cccr & CAN_MCAN_CCCR_CSA) == CAN_MCAN_CCCR_CSA) {
if (k_cycle_get_32() - start_time >
k_ms_to_cyc_ceil32(CAN_INIT_TIMEOUT)) {
can->cccr |= CAN_MCAN_CCCR_CSR;
return -EAGAIN;
}
}
return 0;
}
static int can_enter_init_mode(struct can_mcan_reg *can, k_timeout_t timeout)
{
int64_t start_time;
can->cccr |= CAN_MCAN_CCCR_INIT;
start_time = k_uptime_ticks();
while ((can->cccr & CAN_MCAN_CCCR_INIT) == 0U) {
if (k_uptime_ticks() - start_time > timeout.ticks) {
can->cccr &= ~CAN_MCAN_CCCR_INIT;
return -EAGAIN;
}
}
return 0;
}
static int can_leave_init_mode(struct can_mcan_reg *can, k_timeout_t timeout)
{
int64_t start_time;
can->cccr &= ~CAN_MCAN_CCCR_INIT;
start_time = k_uptime_ticks();
while ((can->cccr & CAN_MCAN_CCCR_INIT) != 0U) {
if (k_uptime_ticks() - start_time > timeout.ticks) {
return -EAGAIN;
}
}
return 0;
}
void can_mcan_configure_timing(struct can_mcan_reg *can,
const struct can_timing *timing,
const struct can_timing *timing_data)
{
if (timing) {
uint32_t nbtp_sjw = can->nbtp & CAN_MCAN_NBTP_NSJW_MSK;
__ASSERT_NO_MSG(timing->prop_seg == 0);
__ASSERT_NO_MSG(timing->phase_seg1 <= 0x100 &&
timing->phase_seg1 > 0);
__ASSERT_NO_MSG(timing->phase_seg2 <= 0x80 &&
timing->phase_seg2 > 0);
__ASSERT_NO_MSG(timing->prescaler <= 0x200 &&
timing->prescaler > 0);
__ASSERT_NO_MSG(timing->sjw == CAN_SJW_NO_CHANGE ||
(timing->sjw <= 0x80 && timing->sjw > 0));
can->nbtp = (((uint32_t)timing->phase_seg1 - 1UL) & 0xFF) <<
CAN_MCAN_NBTP_NTSEG1_POS |
(((uint32_t)timing->phase_seg2 - 1UL) & 0x7F) <<
CAN_MCAN_NBTP_NTSEG2_POS |
(((uint32_t)timing->prescaler - 1UL) & 0x1FF) <<
CAN_MCAN_NBTP_NBRP_POS;
if (timing->sjw == CAN_SJW_NO_CHANGE) {
can->nbtp |= nbtp_sjw;
} else {
can->nbtp |= (((uint32_t)timing->sjw - 1UL) & 0x7F) <<
CAN_MCAN_NBTP_NSJW_POS;
}
}
#ifdef CONFIG_CAN_FD_MODE
if (timing_data) {
uint32_t dbtp_sjw = can->dbtp & CAN_MCAN_DBTP_DSJW_MSK;
__ASSERT_NO_MSG(timing_data->prop_seg == 0);
__ASSERT_NO_MSG(timing_data->phase_seg1 <= 0x20 &&
timing_data->phase_seg1 > 0);
__ASSERT_NO_MSG(timing_data->phase_seg2 <= 0x10 &&
timing_data->phase_seg2 > 0);
__ASSERT_NO_MSG(timing_data->prescaler <= 0x20 &&
timing_data->prescaler > 0);
__ASSERT_NO_MSG(timing_data->sjw == CAN_SJW_NO_CHANGE ||
(timing_data->sjw <= 0x80 && timing_data->sjw > 0));
can->dbtp = (((uint32_t)timing_data->phase_seg1 - 1UL) & 0x1F) <<
CAN_MCAN_DBTP_DTSEG1_POS |
(((uint32_t)timing_data->phase_seg2 - 1UL) & 0x0F) <<
CAN_MCAN_DBTP_DTSEG2_POS |
(((uint32_t)timing_data->prescaler - 1UL) & 0x1F) <<
CAN_MCAN_DBTP_DBRP_POS;
if (timing_data->sjw == CAN_SJW_NO_CHANGE) {
can->dbtp |= dbtp_sjw;
} else {
can->dbtp |= (((uint32_t)timing_data->sjw - 1UL) & 0x0F) <<
CAN_MCAN_DBTP_DSJW_POS;
}
}
#endif
}
int can_mcan_set_timing(const struct device *dev,
const struct can_timing *timing)
{
const struct can_mcan_config *cfg = dev->config;
struct can_mcan_reg *can = cfg->can;
int ret;
ret = can_enter_init_mode(can, K_MSEC(CAN_INIT_TIMEOUT));
if (ret) {
LOG_ERR("Failed to enter init mode");
return -EIO;
}
/* Configuration Change Enable */
can->cccr |= CAN_MCAN_CCCR_CCE;
can_mcan_configure_timing(can, timing, NULL);
ret = can_leave_init_mode(can, K_MSEC(CAN_INIT_TIMEOUT));
if (ret) {
LOG_ERR("Failed to leave init mode");
return -EIO;
}
return 0;
}
#ifdef CONFIG_CAN_FD_MODE
int can_mcan_set_timing_data(const struct device *dev,
const struct can_timing *timing_data)
{
const struct can_mcan_config *cfg = dev->config;
struct can_mcan_reg *can = cfg->can;
int ret;
ret = can_enter_init_mode(can, K_MSEC(CAN_INIT_TIMEOUT));
if (ret) {
LOG_ERR("Failed to enter init mode");
return -EIO;
}
/* Configuration Change Enable */
can->cccr |= CAN_MCAN_CCCR_CCE;
can_mcan_configure_timing(can, NULL, timing_data);
ret = can_leave_init_mode(can, K_MSEC(CAN_INIT_TIMEOUT));
if (ret) {
LOG_ERR("Failed to leave init mode");
return -EIO;
}
return 0;
}
#endif /* CONFIG_CAN_FD_MODE */
int can_mcan_get_capabilities(const struct device *dev, can_mode_t *cap)
{
ARG_UNUSED(dev);
*cap = CAN_MODE_NORMAL | CAN_MODE_LOOPBACK | CAN_MODE_LISTENONLY;
#if CONFIG_CAN_FD_MODE
*cap |= CAN_MODE_FD;
#endif /* CONFIG_CAN_FD_MODE */
return 0;
}
int can_mcan_set_mode(const struct device *dev, can_mode_t mode)
{
const struct can_mcan_config *cfg = dev->config;
struct can_mcan_reg *can = cfg->can;
int ret;
#ifdef CONFIG_CAN_FD_MODE
if ((mode & ~(CAN_MODE_LOOPBACK | CAN_MODE_LISTENONLY | CAN_MODE_FD)) != 0) {
LOG_ERR("unsupported mode: 0x%08x", mode);
return -ENOTSUP;
}
#else
if ((mode & ~(CAN_MODE_LOOPBACK | CAN_MODE_LISTENONLY)) != 0) {
LOG_ERR("unsupported mode: 0x%08x", mode);
return -ENOTSUP;
}
#endif /* CONFIG_CAN_FD_MODE */
if (cfg->phy != NULL) {
ret = can_transceiver_enable(cfg->phy);
if (ret != 0) {
LOG_ERR("failed to enable CAN transceiver (err %d)", ret);
return ret;
}
}
ret = can_enter_init_mode(can, K_MSEC(CAN_INIT_TIMEOUT));
if (ret) {
LOG_ERR("Failed to enter init mode");
if (cfg->phy != NULL) {
/* Attempt to disable the CAN transceiver in case of error */
(void)can_transceiver_disable(cfg->phy);
}
return -EIO;
}
/* Configuration Change Enable */
can->cccr |= CAN_MCAN_CCCR_CCE;
if ((mode & CAN_MODE_LOOPBACK) != 0) {
/* Loopback mode */
can->cccr |= CAN_MCAN_CCCR_TEST;
can->test |= CAN_MCAN_TEST_LBCK;
} else {
can->cccr &= ~CAN_MCAN_CCCR_TEST;
}
if ((mode & CAN_MODE_LISTENONLY) != 0) {
/* Bus monitoring mode */
can->cccr |= CAN_MCAN_CCCR_MON;
} else {
can->cccr &= ~CAN_MCAN_CCCR_MON;
}
#ifdef CONFIG_CAN_FD_MODE
if ((mode & CAN_MODE_FD) != 0) {
can->cccr |= CAN_MCAN_CCCR_FDOE | CAN_MCAN_CCCR_BRSE;
} else {
can->cccr &= ~(CAN_MCAN_CCCR_FDOE | CAN_MCAN_CCCR_BRSE);
}
#endif /* CONFIG_CAN_FD_MODE */
ret = can_leave_init_mode(can, K_MSEC(CAN_INIT_TIMEOUT));
if (ret) {
LOG_ERR("Failed to leave init mode");
if (cfg->phy != NULL) {
/* Attempt to disable the CAN transceiver in case of error */
(void)can_transceiver_disable(cfg->phy);
}
}
return 0;
}
int can_mcan_init(const struct device *dev)
{
const struct can_mcan_config *cfg = dev->config;
struct can_mcan_data *data = dev->data;
struct can_mcan_reg *can = cfg->can;
struct can_mcan_msg_sram *msg_ram = data->msg_ram;
struct can_timing timing;
#ifdef CONFIG_CAN_FD_MODE
struct can_timing timing_data;
#endif
int ret;
k_mutex_init(&data->inst_mutex);
k_mutex_init(&data->tx_mtx);
k_sem_init(&data->tx_sem, NUM_TX_BUF_ELEMENTS, NUM_TX_BUF_ELEMENTS);
for (int i = 0; i < ARRAY_SIZE(data->tx_fin_sem); ++i) {
k_sem_init(&data->tx_fin_sem[i], 0, 1);
}
if (cfg->phy != NULL) {
if (!device_is_ready(cfg->phy)) {
LOG_ERR("CAN transceiver not ready");
return -ENODEV;
}
ret = can_transceiver_enable(cfg->phy);
if (ret != 0) {
LOG_ERR("failed to enable CAN transceiver (err %d)", ret);
return -EIO;
}
}
ret = can_exit_sleep_mode(can);
if (ret) {
LOG_ERR("Failed to exit sleep mode");
ret = -EIO;
goto done;
}
ret = can_enter_init_mode(can, K_MSEC(CAN_INIT_TIMEOUT));
if (ret) {
LOG_ERR("Failed to enter init mode");
ret = -EIO;
goto done;
}
/* Configuration Change Enable */
can->cccr |= CAN_MCAN_CCCR_CCE;
LOG_DBG("IP rel: %lu.%lu.%lu %02lu.%lu.%lu",
(can->crel & CAN_MCAN_CREL_REL) >> CAN_MCAN_CREL_REL_POS,
(can->crel & CAN_MCAN_CREL_STEP) >> CAN_MCAN_CREL_STEP_POS,
(can->crel & CAN_MCAN_CREL_SUBSTEP) >>
CAN_MCAN_CREL_SUBSTEP_POS,
(can->crel & CAN_MCAN_CREL_YEAR) >> CAN_MCAN_CREL_YEAR_POS,
(can->crel & CAN_MCAN_CREL_MON) >> CAN_MCAN_CREL_MON_POS,
(can->crel & CAN_MCAN_CREL_DAY) >> CAN_MCAN_CREL_DAY_POS);
#ifndef CONFIG_CAN_STM32FD
uint32_t mrba = 0;
#ifdef CONFIG_CAN_STM32H7
mrba = (uint32_t)msg_ram;
#endif
#ifdef CONFIG_CAN_MCUX_MCAN
mrba = (uint32_t)msg_ram & CAN_MCAN_MRBA_BA_MSK;
can->mrba = mrba;
#endif
can->sidfc = (((uint32_t)msg_ram->std_filt - mrba) & CAN_MCAN_SIDFC_FLSSA_MSK) |
(ARRAY_SIZE(msg_ram->std_filt) << CAN_MCAN_SIDFC_LSS_POS);
can->xidfc = (((uint32_t)msg_ram->ext_filt - mrba) & CAN_MCAN_XIDFC_FLESA_MSK) |
(ARRAY_SIZE(msg_ram->ext_filt) << CAN_MCAN_XIDFC_LSS_POS);
can->rxf0c = (((uint32_t)msg_ram->rx_fifo0 - mrba) & CAN_MCAN_RXF0C_F0SA) |
(ARRAY_SIZE(msg_ram->rx_fifo0) << CAN_MCAN_RXF0C_F0S_POS);
can->rxf1c = (((uint32_t)msg_ram->rx_fifo1 - mrba) & CAN_MCAN_RXF1C_F1SA) |
(ARRAY_SIZE(msg_ram->rx_fifo1) << CAN_MCAN_RXF1C_F1S_POS);
can->rxbc = (((uint32_t)msg_ram->rx_buffer - mrba) & CAN_MCAN_RXBC_RBSA);
can->txefc = (((uint32_t)msg_ram->tx_event_fifo - mrba) & CAN_MCAN_TXEFC_EFSA_MSK) |
(ARRAY_SIZE(msg_ram->tx_event_fifo) << CAN_MCAN_TXEFC_EFS_POS);
can->txbc = (((uint32_t)msg_ram->tx_buffer - mrba) & CAN_MCAN_TXBC_TBSA) |
(ARRAY_SIZE(msg_ram->tx_buffer) << CAN_MCAN_TXBC_TFQS_POS) |
CAN_MCAN_TXBC_TFQM;
if (sizeof(msg_ram->tx_buffer[0].data) <= 24) {
can->txesc = (sizeof(msg_ram->tx_buffer[0].data) - 8) / 4;
} else {
can->txesc = (sizeof(msg_ram->tx_buffer[0].data) - 32) / 16 + 5;
}
if (sizeof(msg_ram->rx_fifo0[0].data) <= 24) {
can->rxesc = (((sizeof(msg_ram->rx_fifo0[0].data) - 8) / 4) <<
CAN_MCAN_RXESC_F0DS_POS) |
(((sizeof(msg_ram->rx_fifo1[0].data) - 8) / 4) <<
CAN_MCAN_RXESC_F1DS_POS) |
(((sizeof(msg_ram->rx_buffer[0].data) - 8) / 4) <<
CAN_MCAN_RXESC_RBDS_POS);
} else {
can->rxesc = (((sizeof(msg_ram->rx_fifo0[0].data) - 32)
/ 16 + 5) << CAN_MCAN_RXESC_F0DS_POS) |
(((sizeof(msg_ram->rx_fifo1[0].data) - 32)
/ 16 + 5) << CAN_MCAN_RXESC_F1DS_POS) |
(((sizeof(msg_ram->rx_buffer[0].data) - 32)
/ 16 + 5) << CAN_MCAN_RXESC_RBDS_POS);
}
#endif
can->cccr &= ~(CAN_MCAN_CCCR_FDOE | CAN_MCAN_CCCR_BRSE |
CAN_MCAN_CCCR_TEST | CAN_MCAN_CCCR_MON |
CAN_MCAN_CCCR_ASM);
can->test &= ~(CAN_MCAN_TEST_LBCK);
#if defined(CONFIG_CAN_DELAY_COMP) && defined(CONFIG_CAN_FD_MODE)
can->dbtp |= CAN_MCAN_DBTP_TDC;
can->tdcr |= cfg->tx_delay_comp_offset << CAN_MCAN_TDCR_TDCO_POS;
#endif
#ifdef CONFIG_CAN_STM32FD
can->rxgfc |= (CONFIG_CAN_MAX_STD_ID_FILTER << CAN_MCAN_RXGFC_LSS_POS) |
(CONFIG_CAN_MAX_EXT_ID_FILTER << CAN_MCAN_RXGFC_LSE_POS) |
(0x2 << CAN_MCAN_RXGFC_ANFS_POS) |
(0x2 << CAN_MCAN_RXGFC_ANFE_POS);
#else
can->gfc |= (0x2 << CAN_MCAN_GFC_ANFE_POS) |
(0x2 << CAN_MCAN_GFC_ANFS_POS);
#endif /* CONFIG_CAN_STM32FD */
if (cfg->sample_point) {
ret = can_calc_timing(dev, &timing, cfg->bus_speed,
cfg->sample_point);
if (ret == -EINVAL) {
LOG_ERR("Can't find timing for given param");
ret = -EIO;
goto done;
}
LOG_DBG("Presc: %d, TS1: %d, TS2: %d",
timing.prescaler, timing.phase_seg1, timing.phase_seg2);
LOG_DBG("Sample-point err : %d", ret);
} else if (cfg->prop_ts1) {
timing.prop_seg = 0;
timing.phase_seg1 = cfg->prop_ts1;
timing.phase_seg2 = cfg->ts2;
ret = can_calc_prescaler(dev, &timing, cfg->bus_speed);
if (ret) {
LOG_WRN("Bitrate error: %d", ret);
}
}
#ifdef CONFIG_CAN_FD_MODE
if (cfg->sample_point_data) {
ret = can_calc_timing_data(dev, &timing_data,
cfg->bus_speed_data,
cfg->sample_point_data);
if (ret == -EINVAL) {
LOG_ERR("Can't find timing for given dataphase param");
ret = -EIO;
goto done;
}
LOG_DBG("Sample-point err data phase: %d", ret);
} else if (cfg->prop_ts1_data) {
timing_data.prop_seg = 0;
timing_data.phase_seg1 = cfg->prop_ts1_data;
timing_data.phase_seg2 = cfg->ts2_data;
ret = can_calc_prescaler(dev, &timing_data,
cfg->bus_speed_data);
if (ret) {
LOG_WRN("Dataphase bitrate error: %d", ret);
}
}
#endif
timing.sjw = cfg->sjw;
#ifdef CONFIG_CAN_FD_MODE
timing_data.sjw = cfg->sjw_data;
can_mcan_configure_timing(can, &timing, &timing_data);
#else
can_mcan_configure_timing(can, &timing, NULL);
#endif
can->ie = CAN_MCAN_IE_BO | CAN_MCAN_IE_EW | CAN_MCAN_IE_EP |
CAN_MCAN_IE_MRAF | CAN_MCAN_IE_TEFL | CAN_MCAN_IE_TEFN |
CAN_MCAN_IE_RF0N | CAN_MCAN_IE_RF1N | CAN_MCAN_IE_RF0L |
CAN_MCAN_IE_RF1L;
#ifdef CONFIG_CAN_STM32FD
can->ils = CAN_MCAN_ILS_RXFIFO0 | CAN_MCAN_ILS_RXFIFO1;
#else
can->ils = CAN_MCAN_ILS_RF0N | CAN_MCAN_ILS_RF1N;
#endif
can->ile = CAN_MCAN_ILE_EINT0 | CAN_MCAN_ILE_EINT1;
/* Interrupt on every TX fifo element*/
can->txbtie = CAN_MCAN_TXBTIE_TIE;
memset32_volatile(msg_ram, 0, sizeof(struct can_mcan_msg_sram));
sys_cache_data_range(msg_ram, sizeof(struct can_mcan_msg_sram), K_CACHE_WB);
ret = can_leave_init_mode(can, K_MSEC(CAN_INIT_TIMEOUT));
if (ret) {
LOG_ERR("Failed to leave init mode");
ret = -EIO;
goto done;
}
done:
if (ret != 0 && cfg->phy != NULL) {
/* Attempt to disable the CAN transceiver in case of error */
(void)can_transceiver_disable(cfg->phy);
}
return ret;
}
static void can_mcan_state_change_handler(const struct device *dev)
{
struct can_mcan_data *data = dev->data;
const can_state_change_callback_t cb = data->state_change_cb;
void *cb_data = data->state_change_cb_data;
struct can_bus_err_cnt err_cnt;
enum can_state state;
(void)can_mcan_get_state(dev, &state, &err_cnt);
if (cb != NULL) {
cb(dev, state, err_cnt, cb_data);
}
}
static void can_mcan_tc_event_handler(const struct device *dev)
{
const struct can_mcan_config *cfg = dev->config;
struct can_mcan_data *data = dev->data;
struct can_mcan_reg *can = cfg->can;
struct can_mcan_msg_sram *msg_ram = data->msg_ram;
volatile struct can_mcan_tx_event_fifo *tx_event;
can_tx_callback_t tx_cb;
uint32_t event_idx, tx_idx;
while (can->txefs & CAN_MCAN_TXEFS_EFFL) {
event_idx = (can->txefs & CAN_MCAN_TXEFS_EFGI) >>
CAN_MCAN_TXEFS_EFGI_POS;
sys_cache_data_range((void *)&msg_ram->tx_event_fifo[event_idx],
sizeof(struct can_mcan_tx_event_fifo),
K_CACHE_INVD);
tx_event = &msg_ram->tx_event_fifo[event_idx];
tx_idx = tx_event->mm.idx;
/* Acknowledge TX event */
can->txefa = event_idx;
k_sem_give(&data->tx_sem);
tx_cb = data->tx_fin_cb[tx_idx];
if (tx_cb == NULL) {
k_sem_give(&data->tx_fin_sem[tx_idx]);
} else {
tx_cb(dev, 0, data->tx_fin_cb_arg[tx_idx]);
}
}
}
void can_mcan_line_0_isr(const struct device *dev)
{
const struct can_mcan_config *cfg = dev->config;
struct can_mcan_data *data = dev->data;
struct can_mcan_reg *can = cfg->can;
do {
if (can->ir & (CAN_MCAN_IR_BO | CAN_MCAN_IR_EP |
CAN_MCAN_IR_EW)) {
can->ir = CAN_MCAN_IR_BO | CAN_MCAN_IR_EP |
CAN_MCAN_IR_EW;
can_mcan_state_change_handler(dev);
}
/* TX event FIFO new entry */
if (can->ir & CAN_MCAN_IR_TEFN) {
can->ir = CAN_MCAN_IR_TEFN;
can_mcan_tc_event_handler(dev);
}
if (can->ir & CAN_MCAN_IR_TEFL) {
can->ir = CAN_MCAN_IR_TEFL;
LOG_ERR("TX FIFO element lost");
k_sem_give(&data->tx_sem);
}
if (can->ir & CAN_MCAN_IR_ARA) {
can->ir = CAN_MCAN_IR_ARA;
LOG_ERR("Access to reserved address");
}
if (can->ir & CAN_MCAN_IR_MRAF) {
can->ir = CAN_MCAN_IR_MRAF;
LOG_ERR("Message RAM access failure");
}
} while (can->ir & (CAN_MCAN_IR_BO | CAN_MCAN_IR_EW | CAN_MCAN_IR_EP |
CAN_MCAN_IR_TEFL | CAN_MCAN_IR_TEFN));
}
static void can_mcan_get_message(const struct device *dev,
volatile struct can_mcan_rx_fifo *fifo,
volatile uint32_t *fifo_status_reg,
volatile uint32_t *fifo_ack_reg)
{
struct can_mcan_data *data = dev->data;
uint32_t get_idx, filt_idx;
struct zcan_frame frame;
can_rx_callback_t cb;
int data_length;
void *cb_arg;
struct can_mcan_rx_fifo_hdr hdr;
bool rtr_filter_mask;
bool rtr_filter;
while ((*fifo_status_reg & CAN_MCAN_RXF0S_F0FL)) {
get_idx = (*fifo_status_reg & CAN_MCAN_RXF0S_F0GI) >>
CAN_MCAN_RXF0S_F0GI_POS;
sys_cache_data_range((void *)&fifo[get_idx].hdr,
sizeof(struct can_mcan_rx_fifo_hdr),
K_CACHE_INVD);
memcpy32_volatile(&hdr, &fifo[get_idx].hdr,
sizeof(struct can_mcan_rx_fifo_hdr));
if (hdr.xtd) {
frame.id = hdr.ext_id;
} else {
frame.id = hdr.std_id;
}
frame.fd = hdr.fdf;
frame.rtr = hdr.rtr ? CAN_REMOTEREQUEST :
CAN_DATAFRAME;
frame.id_type = hdr.xtd ? CAN_EXTENDED_IDENTIFIER :
CAN_STANDARD_IDENTIFIER;
frame.dlc = hdr.dlc;
frame.brs = hdr.brs;
#if defined(CONFIG_CAN_RX_TIMESTAMP)
frame.timestamp = hdr.rxts;
#endif
filt_idx = hdr.fidx;
if (hdr.xtd != 0) {
rtr_filter_mask = (data->ext_filt_rtr_mask & BIT(filt_idx)) != 0;
rtr_filter = (data->ext_filt_rtr & BIT(filt_idx)) != 0;
} else {
rtr_filter_mask = (data->std_filt_rtr_mask & BIT(filt_idx)) != 0;
rtr_filter = (data->std_filt_rtr & BIT(filt_idx)) != 0;
}
if (rtr_filter_mask && (rtr_filter != frame.rtr)) {
/* RTR bit does not match filter RTR mask and bit, drop frame */
*fifo_ack_reg = get_idx;
continue;
}
data_length = can_dlc_to_bytes(frame.dlc);
if (data_length <= sizeof(frame.data)) {
/* data needs to be written in 32 bit blocks!*/
sys_cache_data_range((void *)fifo[get_idx].data_32,
ROUND_UP(data_length, sizeof(uint32_t)),
K_CACHE_INVD);
memcpy32_volatile(frame.data_32, fifo[get_idx].data_32,
ROUND_UP(data_length, sizeof(uint32_t)));
if (frame.id_type == CAN_STANDARD_IDENTIFIER) {
LOG_DBG("Frame on filter %d, ID: 0x%x",
filt_idx, frame.id);
cb = data->rx_cb_std[filt_idx];
cb_arg = data->cb_arg_std[filt_idx];
} else {
LOG_DBG("Frame on filter %d, ID: 0x%x",
filt_idx + NUM_STD_FILTER_DATA,
frame.id);
cb = data->rx_cb_ext[filt_idx];
cb_arg = data->cb_arg_ext[filt_idx];
}
if (cb) {
cb(dev, &frame, cb_arg);
} else {
LOG_DBG("cb missing");
}
} else {
LOG_ERR("Frame is too big");
}
*fifo_ack_reg = get_idx;
}
}
void can_mcan_line_1_isr(const struct device *dev)
{
const struct can_mcan_config *cfg = dev->config;
struct can_mcan_data *data = dev->data;
struct can_mcan_reg *can = cfg->can;
struct can_mcan_msg_sram *msg_ram = data->msg_ram;
do {
if (can->ir & CAN_MCAN_IR_RF0N) {
can->ir = CAN_MCAN_IR_RF0N;
LOG_DBG("RX FIFO0 INT");
can_mcan_get_message(dev, msg_ram->rx_fifo0,
&can->rxf0s, &can->rxf0a);
}
if (can->ir & CAN_MCAN_IR_RF1N) {
can->ir = CAN_MCAN_IR_RF1N;
LOG_DBG("RX FIFO1 INT");
can_mcan_get_message(dev, msg_ram->rx_fifo1,
&can->rxf1s, &can->rxf1a);
}
if (can->ir & CAN_MCAN_IR_RF0L) {
can->ir = CAN_MCAN_IR_RF0L;
LOG_ERR("Message lost on FIFO0");
}
if (can->ir & CAN_MCAN_IR_RF1L) {
can->ir = CAN_MCAN_IR_RF1L;
LOG_ERR("Message lost on FIFO1");
}
} while (can->ir & (CAN_MCAN_IR_RF0N | CAN_MCAN_IR_RF1N |
CAN_MCAN_IR_RF0L | CAN_MCAN_IR_RF1L));
}
int can_mcan_get_state(const struct device *dev, enum can_state *state,
struct can_bus_err_cnt *err_cnt)
{
const struct can_mcan_config *cfg = dev->config;
struct can_mcan_reg *can = cfg->can;
if (state != NULL) {
if (can->psr & CAN_MCAN_PSR_BO) {
*state = CAN_BUS_OFF;
} else if (can->psr & CAN_MCAN_PSR_EP) {
*state = CAN_ERROR_PASSIVE;
} else if (can->psr & CAN_MCAN_PSR_EW) {
*state = CAN_ERROR_WARNING;
} else {
*state = CAN_ERROR_ACTIVE;
}
}
if (err_cnt != NULL) {
err_cnt->rx_err_cnt = (can->ecr & CAN_MCAN_ECR_TEC_MSK) <<
CAN_MCAN_ECR_TEC_POS;
err_cnt->tx_err_cnt = (can->ecr & CAN_MCAN_ECR_REC_MSK) <<
CAN_MCAN_ECR_REC_POS;
}
return 0;
}
#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY
int can_mcan_recover(const struct device *dev, k_timeout_t timeout)
{
const struct can_mcan_config *cfg = dev->config;
struct can_mcan_reg *can = cfg->can;
return can_leave_init_mode(can, timeout);
}
#endif /* CONFIG_CAN_AUTO_BUS_OFF_RECOVERY */
int can_mcan_send(const struct device *dev,
const struct zcan_frame *frame,
k_timeout_t timeout,
can_tx_callback_t callback, void *user_data)
{
const struct can_mcan_config *cfg = dev->config;
struct can_mcan_data *data = dev->data;
struct can_mcan_reg *can = cfg->can;
struct can_mcan_msg_sram *msg_ram = data->msg_ram;
size_t data_length = can_dlc_to_bytes(frame->dlc);
struct can_mcan_tx_buffer_hdr tx_hdr = {
.rtr = frame->rtr == CAN_REMOTEREQUEST,
.xtd = frame->id_type == CAN_EXTENDED_IDENTIFIER,
.esi = 0,
.dlc = frame->dlc,
#ifdef CONFIG_CAN_FD_MODE
.brs = frame->brs == true,
#endif
.fdf = frame->fd,
.efc = 1,
};
uint32_t put_idx;
int ret;
struct can_mcan_mm mm;
LOG_DBG("Sending %d bytes. Id: 0x%x, ID type: %s %s %s %s",
data_length, frame->id,
frame->id_type == CAN_STANDARD_IDENTIFIER ?
"standard" : "extended",
frame->rtr == CAN_DATAFRAME ? "" : "RTR",
frame->fd == CAN_DATAFRAME ? "" : "FD frame",
frame->brs == CAN_DATAFRAME ? "" : "BRS");
if (data_length > sizeof(frame->data)) {
LOG_ERR("data length (%zu) > max frame data length (%zu)",
data_length, sizeof(frame->data));
return -EINVAL;
}
if (frame->fd != 1 && frame->dlc > MCAN_MAX_DLC) {
LOG_ERR("DLC of %d without fd flag set.", frame->dlc);
return -EINVAL;
}
if (can->psr & CAN_MCAN_PSR_BO) {
return -ENETDOWN;
}
ret = k_sem_take(&data->tx_sem, timeout);
if (ret != 0) {
return -EAGAIN;
}
__ASSERT_NO_MSG((can->txfqs & CAN_MCAN_TXFQS_TFQF) !=
CAN_MCAN_TXFQS_TFQF);
k_mutex_lock(&data->tx_mtx, K_FOREVER);
put_idx = ((can->txfqs & CAN_MCAN_TXFQS_TFQPI) >>
CAN_MCAN_TXFQS_TFQPI_POS);
mm.idx = put_idx;
mm.cnt = data->mm.cnt++;
tx_hdr.mm = mm;
if (frame->id_type == CAN_STANDARD_IDENTIFIER) {
tx_hdr.std_id = frame->id & CAN_STD_ID_MASK;
} else {
tx_hdr.ext_id = frame->id;
}
memcpy32_volatile(&msg_ram->tx_buffer[put_idx].hdr, &tx_hdr, sizeof(tx_hdr));
memcpy32_volatile(msg_ram->tx_buffer[put_idx].data_32, frame->data_32,
ROUND_UP(data_length, 4));
sys_cache_data_range((void *)&msg_ram->tx_buffer[put_idx].hdr, sizeof(tx_hdr), K_CACHE_WB);
sys_cache_data_range((void *)&msg_ram->tx_buffer[put_idx].data_32, ROUND_UP(data_length, 4),
K_CACHE_WB);
data->tx_fin_cb[put_idx] = callback;
data->tx_fin_cb_arg[put_idx] = user_data;
can->txbar = (1U << put_idx);
k_mutex_unlock(&data->tx_mtx);
if (callback == NULL) {
LOG_DBG("Waiting for TX complete");
k_sem_take(&data->tx_fin_sem[put_idx], K_FOREVER);
}
return 0;
}
static int can_mcan_get_free_std(volatile struct can_mcan_std_filter *filters)
{
for (int i = 0; i < NUM_STD_FILTER_DATA; ++i) {
if (filters[i].sfce == CAN_MCAN_FCE_DISABLE) {
return i;
}
}
return -ENOSPC;
}
int can_mcan_get_max_filters(const struct device *dev, enum can_ide id_type)
{
ARG_UNUSED(dev);
if (id_type == CAN_STANDARD_IDENTIFIER) {
return NUM_STD_FILTER_DATA;
} else {
return NUM_EXT_FILTER_DATA;
}
}
/* Use masked configuration only for simplicity. If someone needs more than
* 28 standard filters, dual mode needs to be implemented.
* Dual mode gets tricky, because we can only activate both filters.
* If one of the IDs is not used anymore, we would need to mark it as unused.
*/
int can_mcan_add_rx_filter_std(const struct device *dev,
can_rx_callback_t callback, void *user_data,
const struct zcan_filter *filter)
{
struct can_mcan_data *data = dev->data;
struct can_mcan_msg_sram *msg_ram = data->msg_ram;
struct can_mcan_std_filter filter_element = {
.id1 = filter->id,
.id2 = filter->id_mask,
.sft = CAN_MCAN_SFT_MASKED
};
int filter_id;
k_mutex_lock(&data->inst_mutex, K_FOREVER);
filter_id = can_mcan_get_free_std(msg_ram->std_filt);
if (filter_id == -ENOSPC) {
LOG_WRN("No free standard id filter left");
return -ENOSPC;
}
/* TODO proper fifo balancing */
filter_element.sfce = filter_id & 0x01 ? CAN_MCAN_FCE_FIFO1 :
CAN_MCAN_FCE_FIFO0;
memcpy32_volatile(&msg_ram->std_filt[filter_id], &filter_element,
sizeof(struct can_mcan_std_filter));
sys_cache_data_range((void *)&msg_ram->std_filt[filter_id],
sizeof(struct can_mcan_std_filter),
K_CACHE_WB);
k_mutex_unlock(&data->inst_mutex);
LOG_DBG("Attached std filter at %d", filter_id);
if (filter->rtr) {
data->std_filt_rtr |= (1U << filter_id);
} else {
data->std_filt_rtr &= ~(1U << filter_id);
}
if (filter->rtr_mask) {
data->std_filt_rtr_mask |= (1U << filter_id);
} else {
data->std_filt_rtr_mask &= ~(1U << filter_id);
}
data->rx_cb_std[filter_id] = callback;
data->cb_arg_std[filter_id] = user_data;
return filter_id;
}
static int can_mcan_get_free_ext(volatile struct can_mcan_ext_filter *filters)
{
for (int i = 0; i < NUM_EXT_FILTER_DATA; ++i) {
if (filters[i].efce == CAN_MCAN_FCE_DISABLE) {
return i;
}
}
return -ENOSPC;
}
static int can_mcan_add_rx_filter_ext(const struct device *dev,
can_rx_callback_t callback, void *user_data,
const struct zcan_filter *filter)
{
struct can_mcan_data *data = dev->data;
struct can_mcan_msg_sram *msg_ram = data->msg_ram;
struct can_mcan_ext_filter filter_element = {
.id2 = filter->id_mask,
.id1 = filter->id,
.eft = CAN_MCAN_EFT_MASKED
};
int filter_id;
k_mutex_lock(&data->inst_mutex, K_FOREVER);
filter_id = can_mcan_get_free_ext(msg_ram->ext_filt);
if (filter_id == -ENOSPC) {
LOG_WRN("No free extended id filter left");
return -ENOSPC;
}
/* TODO proper fifo balancing */
filter_element.efce = filter_id & 0x01 ? CAN_MCAN_FCE_FIFO1 :
CAN_MCAN_FCE_FIFO0;
memcpy32_volatile(&msg_ram->ext_filt[filter_id], &filter_element,
sizeof(struct can_mcan_ext_filter));
sys_cache_data_range((void *)&msg_ram->ext_filt[filter_id],
sizeof(struct can_mcan_ext_filter),
K_CACHE_WB);
k_mutex_unlock(&data->inst_mutex);
LOG_DBG("Attached ext filter at %d", filter_id);
if (filter->rtr) {
data->ext_filt_rtr |= (1U << filter_id);
} else {
data->ext_filt_rtr &= ~(1U << filter_id);
}
if (filter->rtr_mask) {
data->ext_filt_rtr_mask |= (1U << filter_id);
} else {
data->ext_filt_rtr_mask &= ~(1U << filter_id);
}
data->rx_cb_ext[filter_id] = callback;
data->cb_arg_ext[filter_id] = user_data;
return filter_id;
}
int can_mcan_add_rx_filter(const struct device *dev,
can_rx_callback_t callback, void *user_data,
const struct zcan_filter *filter)
{
int filter_id;
if (callback == NULL) {
return -EINVAL;
}
if (filter->id_type == CAN_STANDARD_IDENTIFIER) {
filter_id = can_mcan_add_rx_filter_std(dev, callback, user_data, filter);
} else {
filter_id = can_mcan_add_rx_filter_ext(dev, callback, user_data, filter);
if (filter_id >= 0) {
filter_id += NUM_STD_FILTER_DATA;
}
}
return filter_id;
}
void can_mcan_remove_rx_filter(const struct device *dev, int filter_id)
{
struct can_mcan_data *data = dev->data;
struct can_mcan_msg_sram *msg_ram = data->msg_ram;
k_mutex_lock(&data->inst_mutex, K_FOREVER);
if (filter_id >= NUM_STD_FILTER_DATA) {
filter_id -= NUM_STD_FILTER_DATA;
if (filter_id >= NUM_EXT_FILTER_DATA) {
LOG_ERR("Wrong filter id");
return;
}
memset32_volatile(&msg_ram->ext_filt[filter_id], 0,
sizeof(struct can_mcan_ext_filter));
sys_cache_data_range((void *)&msg_ram->ext_filt[filter_id],
sizeof(struct can_mcan_ext_filter),
K_CACHE_WB);
} else {
memset32_volatile(&msg_ram->std_filt[filter_id], 0,
sizeof(struct can_mcan_std_filter));
sys_cache_data_range((void *)&msg_ram->std_filt[filter_id],
sizeof(struct can_mcan_std_filter),
K_CACHE_WB);
}
k_mutex_unlock(&data->inst_mutex);
}
void can_mcan_set_state_change_callback(const struct device *dev,
can_state_change_callback_t callback,
void *user_data)
{
struct can_mcan_data *data = dev->data;
data->state_change_cb = callback;
data->state_change_cb_data = user_data;
}
int can_mcan_get_max_bitrate(const struct device *dev, uint32_t *max_bitrate)
{
const struct can_mcan_config *cfg = dev->config;
*max_bitrate = cfg->max_bitrate;
return 0;
}