drivers: can: Implement Bosch M_CAN driver
Implementation of the Bosch M_CAN IP driver. This driver is just the base for a specific SoC implementation. Signed-off-by: Alexander Wachter <alexander@wachter.cloud>
This commit is contained in:
parent
2ccbfd661e
commit
1c50ef6c43
12 changed files with 2846 additions and 2 deletions
|
@ -4,6 +4,8 @@ zephyr_sources_ifdef(CONFIG_CAN can_common.c)
|
|||
zephyr_sources_ifdef(CONFIG_CAN_LOOPBACK can_loopback.c)
|
||||
zephyr_sources_ifdef(CONFIG_CAN_MCP2515 can_mcp2515.c)
|
||||
zephyr_sources_ifdef(CONFIG_CAN_STM32 can_stm32.c)
|
||||
zephyr_sources_ifdef(CONFIG_CAN_STM32FD can_stm32fd.c)
|
||||
zephyr_sources_ifdef(CONFIG_CAN_MCAN can_mcan.c)
|
||||
zephyr_sources_ifdef(CONFIG_CAN_MCUX_FLEXCAN can_mcux_flexcan.c)
|
||||
|
||||
zephyr_sources_ifdef(CONFIG_USERSPACE can_handlers.c)
|
||||
|
|
|
@ -24,8 +24,15 @@ config CAN_SHELL
|
|||
help
|
||||
Enable CAN Shell for testing.
|
||||
|
||||
config CAN_FD_MODE
|
||||
config CAN_HAS_CANFD
|
||||
bool
|
||||
help
|
||||
driver supports CAN-FD
|
||||
|
||||
config CAN_FD_MODE
|
||||
bool "CAN-FD"
|
||||
default y
|
||||
depends on CAN_HAS_CANFD
|
||||
help
|
||||
Enable CAN-FD compatible API
|
||||
|
||||
|
@ -59,7 +66,7 @@ config CAN_WORKQ_FRAMES_BUF_CNT
|
|||
|
||||
config CAN_RX_TIMESTAMP
|
||||
bool "Enable receiving timestamps"
|
||||
depends on CAN_STM32 || CAN_MCUX_FLEXCAN
|
||||
depends on CAN_STM32 || CAN_MCUX_FLEXCAN || CAN_STM32FD
|
||||
help
|
||||
This option enables a timestamp value of the CAN free running timer.
|
||||
The value is incremented every bit time and starts when the controller
|
||||
|
@ -75,8 +82,10 @@ config CAN_AUTO_BUS_OFF_RECOVERY
|
|||
available.
|
||||
|
||||
source "drivers/can/Kconfig.stm32"
|
||||
source "drivers/can/Kconfig.stm32fd"
|
||||
source "drivers/can/Kconfig.mcux"
|
||||
source "drivers/can/Kconfig.mcp2515"
|
||||
source "drivers/can/Kconfig.mcan"
|
||||
source "drivers/can/Kconfig.loopback"
|
||||
source "drivers/can/Kconfig.net"
|
||||
|
||||
|
|
22
drivers/can/Kconfig.mcan
Normal file
22
drivers/can/Kconfig.mcan
Normal file
|
@ -0,0 +1,22 @@
|
|||
# Bosch m_can configuration options
|
||||
|
||||
# Copyright (c) 2020 Alexander Wachter
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
config CAN_MCAN
|
||||
bool
|
||||
select CAN_HAS_CANFD
|
||||
help
|
||||
Enable Bosch m_can driver.
|
||||
This driver supports the Bosch m_can IP. This IP is built into the
|
||||
STM32G4, STM32G0 and the Microchip SAM controllers with CAN-FD.
|
||||
|
||||
if CAN_MCAN
|
||||
|
||||
config CAN_DELAY_COMP
|
||||
bool "Enable transceiver delay compensation"
|
||||
default y
|
||||
help
|
||||
Enable the automatic transceiver delay compensation.
|
||||
|
||||
endif #CAN_MCAN
|
35
drivers/can/Kconfig.stm32fd
Normal file
35
drivers/can/Kconfig.stm32fd
Normal file
|
@ -0,0 +1,35 @@
|
|||
# STM32 CAN configuration options
|
||||
if CAN_MCAN && SOC_SERIES_STM32G4X
|
||||
|
||||
config CAN_STM32FD
|
||||
bool
|
||||
default y
|
||||
|
||||
config CAN_MAX_STD_ID_FILTER
|
||||
int "Maximum number of std ID filters"
|
||||
default 28
|
||||
range 0 28
|
||||
help
|
||||
Defines the maximum number of filters with standard ID (11-bit)
|
||||
that can be attached.
|
||||
|
||||
config CAN_MAX_EXT_ID_FILTER
|
||||
int "Maximum number of ext ID filters"
|
||||
default 8
|
||||
range 0 8
|
||||
help
|
||||
Defines the maximum number of filters with extended ID (29-bit)
|
||||
that can be attached.
|
||||
|
||||
config CAN_CKDIV
|
||||
int "CKDIV register value"
|
||||
range 0 15
|
||||
default 0
|
||||
help
|
||||
This value is written to the CKDIV register.
|
||||
The APB clock is divided according to this value before it is feed to
|
||||
CAN core. Note that the the divider affects all CAN controllers.
|
||||
The values of the register are multiplied by two and zero corresponds
|
||||
to one. The value six, for example results in a clock divided by 12.
|
||||
|
||||
endif #CAN_MCAN
|
875
drivers/can/can_mcan.c
Normal file
875
drivers/can/can_mcan.c
Normal file
|
@ -0,0 +1,875 @@
|
|||
/*
|
||||
* Copyright (c) 2020 Alexander Wachter
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <sys/util.h>
|
||||
#include <string.h>
|
||||
#include <kernel.h>
|
||||
#include <drivers/can.h>
|
||||
#include "can_mcan.h"
|
||||
#include "can_mcan_int.h"
|
||||
|
||||
#include <logging/log.h>
|
||||
LOG_MODULE_DECLARE(can_driver, 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 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 CAN_TIMEOUT;
|
||||
}
|
||||
}
|
||||
|
||||
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 CAN_TIMEOUT;
|
||||
}
|
||||
}
|
||||
|
||||
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 CAN_TIMEOUT;
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
__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);
|
||||
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->sjw - 1UL) & 0x7F) <<
|
||||
CAN_MCAN_NBTP_NSJW_POS |
|
||||
(((uint32_t)timing->prescaler - 1UL) & 0x1FF) <<
|
||||
CAN_MCAN_NBTP_NBRP_POS;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_CAN_FD_MODE
|
||||
if (timing_data) {
|
||||
__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 <= 20 &&
|
||||
timing_data->prescaler > 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->sjw - 1UL) & 0x0F) <<
|
||||
CAN_MCAN_DBTP_DSJW_POS |
|
||||
(((uint32_t)timing_data->prescaler - 1UL) & 0x1F) <<
|
||||
CAN_MCAN_DBTP_DBRP_POS;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
int can_mcan_set_timing(const struct can_mcan_config *cfg,
|
||||
const struct can_timing *timing,
|
||||
const struct can_timing *timing_data)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
can_mcan_configure_timing(can, timing, 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;
|
||||
}
|
||||
|
||||
int can_mcan_set_mode(const struct can_mcan_config *cfg, enum can_mode mode)
|
||||
{
|
||||
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;
|
||||
|
||||
switch (mode) {
|
||||
case CAN_NORMAL_MODE:
|
||||
LOG_DBG("Config normal mode");
|
||||
can->cccr &= ~(CAN_MCAN_CCCR_TEST | CAN_MCAN_CCCR_MON);
|
||||
break;
|
||||
|
||||
case CAN_SILENT_MODE:
|
||||
LOG_DBG("Config silent mode");
|
||||
can->cccr &= ~CAN_MCAN_CCCR_TEST;
|
||||
can->cccr |= CAN_MCAN_CCCR_MON;
|
||||
break;
|
||||
|
||||
case CAN_LOOPBACK_MODE:
|
||||
LOG_DBG("Config loopback mode");
|
||||
can->cccr &= ~CAN_MCAN_CCCR_MON;
|
||||
can->cccr |= CAN_MCAN_CCCR_TEST;
|
||||
can->test |= CAN_MCAN_TEST_LBCK;
|
||||
break;
|
||||
|
||||
case CAN_SILENT_LOOPBACK_MODE:
|
||||
LOG_DBG("Config silent loopback mode");
|
||||
can->cccr |= (CAN_MCAN_CCCR_TEST | CAN_MCAN_CCCR_MON);
|
||||
can->test |= CAN_MCAN_TEST_LBCK;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
ret = can_leave_init_mode(can, K_MSEC(CAN_INIT_TIMEOUT));
|
||||
if (ret) {
|
||||
LOG_ERR("Failed to leave init mode");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int can_mcan_init(const struct device *dev, const struct can_mcan_config *cfg,
|
||||
struct can_mcan_msg_sram *msg_ram,
|
||||
struct can_mcan_data *data)
|
||||
{
|
||||
struct can_mcan_reg *can = cfg->can;
|
||||
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);
|
||||
}
|
||||
|
||||
ret = can_exit_sleep_mode(can);
|
||||
if (ret) {
|
||||
LOG_ERR("Failed to exit sleep mode");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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
|
||||
can->sidfc = ((uint32_t)msg_ram->std_filt & CAN_MCAN_SIDFC_FLSSA_MSK) |
|
||||
(ARRAY_SIZE(msg_ram->std_filt) << CAN_MCAN_SIDFC_LSS_POS);
|
||||
can->xidfc = ((uint32_t)msg_ram->ext_filt & CAN_MCAN_XIDFC_FLESA_MSK) |
|
||||
(ARRAY_SIZE(msg_ram->ext_filt) << CAN_MCAN_XIDFC_LSS_POS);
|
||||
can->rxf0c = ((uint32_t)msg_ram->rx_fifo0 & CAN_MCAN_RXF0C_F0SA) |
|
||||
(ARRAY_SIZE(msg_ram->rx_fifo0) << CAN_MCAN_RXF0C_F0S_POS);
|
||||
can->rxf1c = ((uint32_t)msg_ram->rx_fifo1 & CAN_MCAN_RXF1C_F1SA) |
|
||||
(ARRAY_SIZE(msg_ram->rx_fifo1) << CAN_MCAN_RXF1C_F1S_POS);
|
||||
can->rxbc = ((uint32_t)msg_ram->rx_buffer & CAN_MCAN_RXBC_RBSA);
|
||||
can->txefc = ((uint32_t)msg_ram->tx_event_fifo & CAN_MCAN_TXEFC_EFSA_MSK) |
|
||||
(ARRAY_SIZE(msg_ram->tx_event_fifo) <<
|
||||
CAN_MCAN_TXEFC_EFS_POS);
|
||||
can->txbc = ((uint32_t)msg_ram->tx_fifo & CAN_MCAN_TXBC_TBSA) |
|
||||
(ARRAY_SIZE(msg_ram->tx_fifo) << CAN_MCAN_TXBC_TFQS_POS);
|
||||
if (sizeof(msg_ram->tx_fifo[0].data) <= 24) {
|
||||
can->txesc = (sizeof(msg_ram->tx_fifo[0].data) - 8) / 4;
|
||||
} else {
|
||||
can->txesc = (sizeof(msg_ram->tx_fifo[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
|
||||
|
||||
#ifdef CONFIG_CAN_FD_MODE
|
||||
can->cccr |= CAN_MCAN_CCCR_FDOE | CAN_MCAN_CCCR_BRSE;
|
||||
#else
|
||||
can->cccr &= ~(CAN_MCAN_CCCR_FDOE | CAN_MCAN_CCCR_BRSE);
|
||||
#endif
|
||||
can->cccr &= ~(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");
|
||||
return -EIO;
|
||||
}
|
||||
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");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
#ifdef CONFIG_CAN_FD_MODE
|
||||
timing_data.sjw = cfg->sjw_data;
|
||||
can_mcan_configure_timing(can, &timing, &timing_data);
|
||||
#else
|
||||
timing.sjw = cfg->sjw;
|
||||
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;
|
||||
|
||||
ret = can_leave_init_mode(can, K_MSEC(CAN_INIT_TIMEOUT));
|
||||
if (ret) {
|
||||
LOG_ERR("Failed to leave init mode");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* No memset because only aligned ptr are allowed */
|
||||
for (uint32_t *ptr = (uint32_t *)msg_ram;
|
||||
ptr < (uint32_t *)msg_ram +
|
||||
sizeof(struct can_mcan_msg_sram) / sizeof(uint32_t);
|
||||
ptr++) {
|
||||
*ptr = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void can_mcan_state_change_handler(const struct can_mcan_config *cfg,
|
||||
struct can_mcan_data *data)
|
||||
{
|
||||
enum can_state state;
|
||||
struct can_bus_err_cnt err_cnt;
|
||||
|
||||
state = can_mcan_get_state(cfg, &err_cnt);
|
||||
|
||||
if (data->state_change_isr) {
|
||||
data->state_change_isr(state, err_cnt);
|
||||
}
|
||||
}
|
||||
|
||||
static void can_mcan_tc_event_handler(struct can_mcan_reg *can,
|
||||
struct can_mcan_msg_sram *msg_ram,
|
||||
struct can_mcan_data *data)
|
||||
{
|
||||
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;
|
||||
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(CAN_TX_OK, data->tx_fin_cb_arg[tx_idx]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void can_mcan_line_0_isr(const struct can_mcan_config *cfg,
|
||||
struct can_mcan_msg_sram *msg_ram,
|
||||
struct can_mcan_data *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(cfg, data);
|
||||
}
|
||||
/* TX event FIFO new entry */
|
||||
if (can->ir & CAN_MCAN_IR_TEFN) {
|
||||
can->ir = CAN_MCAN_IR_TEFN;
|
||||
can_mcan_tc_event_handler(can, msg_ram, data);
|
||||
}
|
||||
|
||||
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(struct can_mcan_data *data,
|
||||
volatile struct can_mcan_rx_fifo *fifo,
|
||||
volatile uint32_t *fifo_status_reg,
|
||||
volatile uint32_t *fifo_ack_reg)
|
||||
{
|
||||
uint32_t get_idx, filt_idx;
|
||||
struct zcan_frame frame;
|
||||
can_rx_callback_t cb;
|
||||
volatile uint32_t *src, *dst, *end;
|
||||
int data_length;
|
||||
void *cb_arg;
|
||||
struct can_mcan_rx_fifo_hdr hdr;
|
||||
|
||||
while ((*fifo_status_reg & CAN_MCAN_RXF0S_F0FL)) {
|
||||
get_idx = (*fifo_status_reg & CAN_MCAN_RXF0S_F0GI) >>
|
||||
CAN_MCAN_RXF0S_F0GI_POS;
|
||||
hdr = fifo[get_idx].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;
|
||||
|
||||
/* Check if RTR must match */
|
||||
if ((hdr.xtd && data->ext_filt_rtr_mask & (1U << filt_idx) &&
|
||||
((data->ext_filt_rtr >> filt_idx) & 1U) != frame.rtr) ||
|
||||
(data->std_filt_rtr_mask & (1U << filt_idx) &&
|
||||
((data->std_filt_rtr >> filt_idx) & 1U) != frame.rtr)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
data_length = can_dlc_to_bytes(frame.dlc);
|
||||
if (data_length <= sizeof(frame.data)) {
|
||||
/* data needs to be written in 32 bit blocks!*/
|
||||
for (src = fifo[get_idx].data_32,
|
||||
dst = frame.data_32,
|
||||
end = dst + CAN_DIV_CEIL(data_length, sizeof(uint32_t));
|
||||
dst < end;
|
||||
src++, dst++) {
|
||||
*dst = *src;
|
||||
}
|
||||
|
||||
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(&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 can_mcan_config *cfg,
|
||||
struct can_mcan_msg_sram *msg_ram,
|
||||
struct can_mcan_data *data)
|
||||
{
|
||||
struct can_mcan_reg *can = cfg->can;
|
||||
|
||||
do {
|
||||
if (can->ir & CAN_MCAN_IR_RF0N) {
|
||||
can->ir = CAN_MCAN_IR_RF0N;
|
||||
LOG_DBG("RX FIFO0 INT");
|
||||
can_mcan_get_message(data, 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(data, 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));
|
||||
}
|
||||
|
||||
enum can_state can_mcan_get_state(const struct can_mcan_config *cfg,
|
||||
struct can_bus_err_cnt *err_cnt)
|
||||
{
|
||||
struct can_mcan_reg *can = cfg->can;
|
||||
|
||||
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;
|
||||
|
||||
if (can->psr & CAN_MCAN_PSR_BO) {
|
||||
return CAN_BUS_OFF;
|
||||
}
|
||||
|
||||
if (can->psr & CAN_MCAN_PSR_EP) {
|
||||
return CAN_ERROR_PASSIVE;
|
||||
}
|
||||
|
||||
return CAN_ERROR_ACTIVE;
|
||||
}
|
||||
|
||||
#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY
|
||||
int can_mcan_recover(struct can_mcan_reg *can, k_timeout_t timeout)
|
||||
{
|
||||
return can_leave_init_mode(can, timeout);
|
||||
}
|
||||
#endif /* CONFIG_CAN_AUTO_BUS_OFF_RECOVERY */
|
||||
|
||||
|
||||
int can_mcan_send(const struct can_mcan_config *cfg,
|
||||
struct can_mcan_data *data,
|
||||
struct can_mcan_msg_sram *msg_ram,
|
||||
const struct zcan_frame *frame,
|
||||
k_timeout_t timeout,
|
||||
can_tx_callback_t callback, void *callback_arg)
|
||||
{
|
||||
struct can_mcan_reg *can = cfg->can;
|
||||
size_t data_length = can_dlc_to_bytes(frame->dlc);
|
||||
struct can_mcan_tx_fifo_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;
|
||||
volatile uint32_t *dst, *end;
|
||||
const uint32_t *src;
|
||||
|
||||
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 CAN_TX_EINVAL;
|
||||
}
|
||||
|
||||
if (frame->fd != 1 && frame->dlc > MCAN_MAX_DLC) {
|
||||
LOG_ERR("DLC of %d without fd flag set.", frame->dlc);
|
||||
return CAN_TX_EINVAL;
|
||||
}
|
||||
|
||||
if (can->psr & CAN_MCAN_PSR_BO) {
|
||||
return CAN_TX_BUS_OFF;
|
||||
}
|
||||
|
||||
ret = k_sem_take(&data->tx_sem, timeout);
|
||||
if (ret != 0) {
|
||||
return CAN_TIMEOUT;
|
||||
}
|
||||
|
||||
__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;
|
||||
}
|
||||
|
||||
msg_ram->tx_fifo[put_idx].hdr = tx_hdr;
|
||||
|
||||
for (src = frame->data_32,
|
||||
dst = msg_ram->tx_fifo[put_idx].data_32,
|
||||
end = dst + CAN_DIV_CEIL(data_length, sizeof(uint32_t));
|
||||
dst < end;
|
||||
src++, dst++) {
|
||||
*dst = *src;
|
||||
}
|
||||
|
||||
data->tx_fin_cb[put_idx] = callback;
|
||||
data->tx_fin_cb_arg[put_idx] = callback_arg;
|
||||
|
||||
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 CAN_TX_OK;
|
||||
}
|
||||
|
||||
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 CAN_NO_FREE_FILTER;
|
||||
}
|
||||
|
||||
/* 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_attach_std(struct can_mcan_data *data,
|
||||
struct can_mcan_msg_sram *msg_ram,
|
||||
can_rx_callback_t isr, void *cb_arg,
|
||||
const struct zcan_filter *filter)
|
||||
{
|
||||
struct can_mcan_std_filter filter_element = {
|
||||
.id1 = filter->id,
|
||||
.id2 = filter->id_mask,
|
||||
.sft = CAN_MCAN_SFT_MASKED
|
||||
};
|
||||
int filter_nr;
|
||||
|
||||
k_mutex_lock(&data->inst_mutex, K_FOREVER);
|
||||
filter_nr = can_mcan_get_free_std(msg_ram->std_filt);
|
||||
|
||||
if (filter_nr == CAN_NO_FREE_FILTER) {
|
||||
LOG_INF("No free standard id filter left");
|
||||
return CAN_NO_FREE_FILTER;
|
||||
}
|
||||
|
||||
/* TODO propper fifo balancing */
|
||||
filter_element.sfce = filter_nr & 0x01 ? CAN_MCAN_FCE_FIFO1 :
|
||||
CAN_MCAN_FCE_FIFO0;
|
||||
|
||||
msg_ram->std_filt[filter_nr] = filter_element;
|
||||
|
||||
k_mutex_unlock(&data->inst_mutex);
|
||||
|
||||
LOG_DBG("Attached std filter at %d", filter_nr);
|
||||
|
||||
if (filter->rtr) {
|
||||
data->std_filt_rtr |= (1U << filter_nr);
|
||||
} else {
|
||||
data->std_filt_rtr &= ~(1U << filter_nr);
|
||||
}
|
||||
|
||||
if (filter->rtr_mask) {
|
||||
data->std_filt_rtr_mask |= (1U << filter_nr);
|
||||
} else {
|
||||
data->std_filt_rtr_mask &= ~(1U << filter_nr);
|
||||
}
|
||||
|
||||
data->rx_cb_std[filter_nr] = isr;
|
||||
data->cb_arg_std[filter_nr] = cb_arg;
|
||||
|
||||
return filter_nr;
|
||||
}
|
||||
|
||||
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 CAN_NO_FREE_FILTER;
|
||||
}
|
||||
|
||||
static int can_mcan_attach_ext(struct can_mcan_data *data,
|
||||
struct can_mcan_msg_sram *msg_ram,
|
||||
can_rx_callback_t isr, void *cb_arg,
|
||||
const struct zcan_filter *filter)
|
||||
{
|
||||
struct can_mcan_ext_filter filter_element = {
|
||||
.id2 = filter->id_mask,
|
||||
.id1 = filter->id,
|
||||
.eft = CAN_MCAN_EFT_MASKED
|
||||
};
|
||||
int filter_nr;
|
||||
|
||||
k_mutex_lock(&data->inst_mutex, K_FOREVER);
|
||||
filter_nr = can_mcan_get_free_ext(msg_ram->ext_filt);
|
||||
|
||||
if (filter_nr == CAN_NO_FREE_FILTER) {
|
||||
LOG_INF("No free extender id filter left");
|
||||
return CAN_NO_FREE_FILTER;
|
||||
}
|
||||
|
||||
/* TODO propper fifo balancing */
|
||||
filter_element.efce = filter_nr & 0x01 ? CAN_MCAN_FCE_FIFO1 :
|
||||
CAN_MCAN_FCE_FIFO0;
|
||||
|
||||
msg_ram->ext_filt[filter_nr] = filter_element;
|
||||
|
||||
k_mutex_unlock(&data->inst_mutex);
|
||||
|
||||
LOG_DBG("Attached ext filter at %d", filter_nr);
|
||||
|
||||
if (filter->rtr) {
|
||||
data->ext_filt_rtr |= (1U << filter_nr);
|
||||
} else {
|
||||
data->ext_filt_rtr &= ~(1U << filter_nr);
|
||||
}
|
||||
|
||||
if (filter->rtr_mask) {
|
||||
data->ext_filt_rtr_mask |= (1U << filter_nr);
|
||||
} else {
|
||||
data->ext_filt_rtr_mask &= ~(1U << filter_nr);
|
||||
}
|
||||
|
||||
data->rx_cb_ext[filter_nr] = isr;
|
||||
data->cb_arg_ext[filter_nr] = cb_arg;
|
||||
|
||||
return filter_nr;
|
||||
}
|
||||
|
||||
int can_mcan_attach_isr(struct can_mcan_data *data,
|
||||
struct can_mcan_msg_sram *msg_ram,
|
||||
can_rx_callback_t isr, void *cb_arg,
|
||||
const struct zcan_filter *filter)
|
||||
{
|
||||
int filter_nr;
|
||||
|
||||
if (!isr) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (filter->id_type == CAN_STANDARD_IDENTIFIER) {
|
||||
filter_nr = can_mcan_attach_std(data, msg_ram, isr, cb_arg,
|
||||
filter);
|
||||
} else {
|
||||
filter_nr = can_mcan_attach_ext(data, msg_ram, isr, cb_arg,
|
||||
filter);
|
||||
filter_nr += NUM_STD_FILTER_DATA;
|
||||
}
|
||||
|
||||
if (filter_nr == CAN_NO_FREE_FILTER) {
|
||||
LOG_INF("No free filter left");
|
||||
}
|
||||
|
||||
return filter_nr;
|
||||
}
|
||||
|
||||
void can_mcan_detach(struct can_mcan_data *data,
|
||||
struct can_mcan_msg_sram *msg_ram, int filter_nr)
|
||||
{
|
||||
const struct can_mcan_ext_filter ext_filter = {0};
|
||||
const struct can_mcan_std_filter std_filter = {0};
|
||||
|
||||
k_mutex_lock(&data->inst_mutex, K_FOREVER);
|
||||
if (filter_nr >= NUM_STD_FILTER_DATA) {
|
||||
filter_nr -= NUM_STD_FILTER_DATA;
|
||||
if (filter_nr >= NUM_STD_FILTER_DATA) {
|
||||
LOG_ERR("Wrong filter id");
|
||||
return;
|
||||
}
|
||||
|
||||
msg_ram->ext_filt[filter_nr] = ext_filter;
|
||||
data->rx_cb_ext[filter_nr] = NULL;
|
||||
} else {
|
||||
msg_ram->std_filt[filter_nr] = std_filter;
|
||||
data->rx_cb_std[filter_nr] = NULL;
|
||||
}
|
||||
|
||||
k_mutex_unlock(&data->inst_mutex);
|
||||
}
|
233
drivers/can/can_mcan.h
Normal file
233
drivers/can/can_mcan.h
Normal file
|
@ -0,0 +1,233 @@
|
|||
/*
|
||||
* Copyright (c) 2020 Alexander Wachter
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_DRIVERS_CAN_MCAN_H_
|
||||
#define ZEPHYR_DRIVERS_CAN_MCAN_H_
|
||||
|
||||
#define NUM_STD_FILTER_ELEMENTS DT_PROP(DT_PATH(soc, can), std_filter_elements)
|
||||
#define NUM_EXT_FILTER_ELEMENTS DT_PROP(DT_PATH(soc, can), ext_filter_elements)
|
||||
#define NUM_RX_FIFO0_ELEMENTS DT_PROP(DT_PATH(soc, can), rx_fifo0_elements)
|
||||
#define NUM_RX_FIFO1_ELEMENTS DT_PROP(DT_PATH(soc, can), rx_fifo0_elements)
|
||||
#define NUM_RX_BUF_ELEMENTS DT_PROP(DT_PATH(soc, can), rx_buffer_elements)
|
||||
#define NUM_TX_EVENT_FIFO_ELEMENTS \
|
||||
DT_PROP(DT_PATH(soc, can), tx_event_fifo_elements)
|
||||
#define NUM_TX_BUF_ELEMENTS DT_PROP(DT_PATH(soc, can), tx_buffer_elements)
|
||||
|
||||
|
||||
#ifdef CONFIG_CAN_STM32FD
|
||||
#define NUM_STD_FILTER_DATA CONFIG_CAN_MAX_STD_ID_FILTER
|
||||
#define NUM_EXT_FILTER_DATA CONFIG_CAN_MAX_EXT_ID_FILTER
|
||||
#else
|
||||
#define NUM_STD_FILTER_DATA NUM_STD_FILTER_ELEMENTS
|
||||
#define NUM_EXT_FILTER_DATA NUM_EXT_FILTER_ELEMENTS
|
||||
#endif
|
||||
|
||||
struct can_mcan_rx_fifo_hdr {
|
||||
union {
|
||||
struct {
|
||||
volatile uint32_t ext_id : 29; /* Extended Identifier */
|
||||
volatile uint32_t rtr : 1; /* Retmote Transmission Request*/
|
||||
volatile uint32_t xtd : 1; /* Extended identifier */
|
||||
volatile uint32_t esi : 1; /* Error state indicator */
|
||||
};
|
||||
struct {
|
||||
volatile uint32_t pad1 : 18;
|
||||
volatile uint32_t std_id : 11; /* Standard Identifier */
|
||||
volatile uint32_t pad2 : 3;
|
||||
};
|
||||
};
|
||||
|
||||
volatile uint32_t rxts : 16; /* Rx timestamp */
|
||||
volatile uint32_t dlc : 4; /* Data Length Code */
|
||||
volatile uint32_t brs : 1; /* Bit Rate Switch */
|
||||
volatile uint32_t fdf : 1; /* FD Format */
|
||||
volatile uint32_t res : 2; /* Reserved */
|
||||
volatile uint32_t fidx : 7; /* Filter Index */
|
||||
volatile uint32_t anmf : 1; /* Accepted non-matching frame */
|
||||
} __packed;
|
||||
|
||||
struct can_mcan_rx_fifo {
|
||||
struct can_mcan_rx_fifo_hdr hdr;
|
||||
union {
|
||||
volatile uint8_t data[64];
|
||||
volatile uint32_t data_32[16];
|
||||
};
|
||||
} __packed;
|
||||
|
||||
struct can_mcan_mm {
|
||||
volatile uint8_t idx : 5;
|
||||
volatile uint8_t cnt : 3;
|
||||
} __packed;
|
||||
|
||||
struct can_mcan_tx_fifo_hdr {
|
||||
union {
|
||||
struct {
|
||||
volatile uint32_t ext_id : 29; /* Identifier */
|
||||
volatile uint32_t rtr : 1; /* Retmote Transmission Request*/
|
||||
volatile uint32_t xtd : 1; /* Extended identifier */
|
||||
volatile uint32_t esi : 1; /* Error state indicator */
|
||||
};
|
||||
struct {
|
||||
volatile uint32_t pad1 : 18;
|
||||
volatile uint32_t std_id : 11; /* Identifier */
|
||||
volatile uint32_t pad2 : 3;
|
||||
};
|
||||
};
|
||||
volatile uint16_t res1; /* Reserved */
|
||||
volatile uint8_t dlc : 4; /* Data Length Code */
|
||||
volatile uint8_t brs : 1; /* Bit Rate Switch */
|
||||
volatile uint8_t fdf : 1; /* FD Format */
|
||||
volatile uint8_t res2 : 1; /* Reserved */
|
||||
volatile uint8_t efc : 1; /* Event FIFO control (Store Tx events) */
|
||||
struct can_mcan_mm mm; /* Message marker */
|
||||
} __packed;
|
||||
|
||||
struct can_mcan_tx_fifo {
|
||||
struct can_mcan_tx_fifo_hdr hdr;
|
||||
union {
|
||||
volatile uint8_t data[64];
|
||||
volatile uint32_t data_32[16];
|
||||
};
|
||||
} __packed;
|
||||
|
||||
#define CAN_MCAN_TE_TX 0x1 /* TX event */
|
||||
#define CAN_MCAN_TE_TXC 0x2 /* TX event in spite of cancellation */
|
||||
|
||||
struct can_mcan_tx_event_fifo {
|
||||
volatile uint32_t id : 29; /* Identifier */
|
||||
volatile uint32_t rtr : 1; /* Retmote Transmission Request*/
|
||||
volatile uint32_t xtd : 1; /* Extended identifier */
|
||||
volatile uint32_t esi : 1; /* Error state indicator */
|
||||
|
||||
volatile uint16_t txts; /* TX Timestamp */
|
||||
volatile uint8_t dlc : 4; /* Data Length Code */
|
||||
volatile uint8_t brs : 1; /* Bit Rate Switch */
|
||||
volatile uint8_t fdf : 1; /* FD Format */
|
||||
volatile uint8_t et : 2; /* Event type */
|
||||
struct can_mcan_mm mm; /* Message marker */
|
||||
} __packed;
|
||||
|
||||
#define CAN_MCAN_FCE_DISABLE 0x0
|
||||
#define CAN_MCAN_FCE_FIFO0 0x1
|
||||
#define CAN_MCAN_FCE_FIFO1 0x2
|
||||
#define CAN_MCAN_FCE_REJECT 0x3
|
||||
#define CAN_MCAN_FCE_PRIO 0x4
|
||||
#define CAN_MCAN_FCE_PRIO_FIFO0 0x5
|
||||
#define CAN_MCAN_FCE_PRIO_FIFO1 0x7
|
||||
|
||||
#define CAN_MCAN_SFT_RANGE 0x0
|
||||
#define CAN_MCAN_SFT_DUAL 0x1
|
||||
#define CAN_MCAN_SFT_MASKED 0x2
|
||||
#define CAN_MCAN_SFT_DISABLED 0x3
|
||||
|
||||
struct can_mcan_std_filter {
|
||||
volatile uint32_t id2 : 11; /* ID2 for dual or range, mask otherwise */
|
||||
volatile uint32_t res : 5;
|
||||
volatile uint32_t id1 : 11;
|
||||
volatile uint32_t sfce : 3; /* Filter config */
|
||||
volatile uint32_t sft : 2; /* Filter type */
|
||||
} __packed;
|
||||
|
||||
#define CAN_MCAN_EFT_RANGE_XIDAM 0x0
|
||||
#define CAN_MCAN_EFT_DUAL 0x1
|
||||
#define CAN_MCAN_EFT_MASKED 0x2
|
||||
#define CAN_MCAN_EFT_RANGE 0x3
|
||||
|
||||
struct can_mcan_ext_filter {
|
||||
volatile uint32_t id1 : 29;
|
||||
volatile uint32_t efce : 3; /* Filter config */
|
||||
volatile uint32_t id2 : 29; /* ID2 for dual or range, mask otherwise */
|
||||
volatile uint32_t res : 1;
|
||||
volatile uint32_t eft : 2; /* Filter type */
|
||||
} __packed;
|
||||
|
||||
struct can_mcan_msg_sram {
|
||||
volatile struct can_mcan_std_filter std_filt[NUM_STD_FILTER_ELEMENTS];
|
||||
volatile struct can_mcan_ext_filter ext_filt[NUM_EXT_FILTER_ELEMENTS];
|
||||
volatile struct can_mcan_rx_fifo rx_fifo0[NUM_RX_FIFO0_ELEMENTS];
|
||||
volatile struct can_mcan_rx_fifo rx_fifo1[NUM_RX_FIFO1_ELEMENTS];
|
||||
volatile struct can_mcan_rx_fifo rx_buffer[NUM_RX_BUF_ELEMENTS];
|
||||
volatile struct can_mcan_tx_event_fifo tx_event_fifo[NUM_TX_BUF_ELEMENTS];
|
||||
volatile struct can_mcan_tx_fifo tx_fifo[NUM_TX_BUF_ELEMENTS];
|
||||
} __packed;
|
||||
|
||||
struct can_mcan_data {
|
||||
struct k_mutex inst_mutex;
|
||||
struct k_sem tx_sem;
|
||||
struct k_mutex tx_mtx;
|
||||
struct k_sem tx_fin_sem[NUM_TX_BUF_ELEMENTS];
|
||||
can_tx_callback_t tx_fin_cb[NUM_TX_BUF_ELEMENTS];
|
||||
void *tx_fin_cb_arg[NUM_TX_BUF_ELEMENTS];
|
||||
can_rx_callback_t rx_cb_std[NUM_STD_FILTER_DATA];
|
||||
can_rx_callback_t rx_cb_ext[NUM_EXT_FILTER_DATA];
|
||||
void *cb_arg_std[NUM_STD_FILTER_DATA];
|
||||
void *cb_arg_ext[NUM_EXT_FILTER_DATA];
|
||||
can_state_change_isr_t state_change_isr;
|
||||
uint32_t std_filt_rtr;
|
||||
uint32_t std_filt_rtr_mask;
|
||||
uint8_t ext_filt_rtr;
|
||||
uint8_t ext_filt_rtr_mask;
|
||||
struct can_mcan_mm mm;
|
||||
} __aligned(4);
|
||||
|
||||
struct can_mcan_config {
|
||||
struct can_mcan_reg *can; /*!< CAN Registers*/
|
||||
uint32_t bus_speed;
|
||||
uint32_t bus_speed_data;
|
||||
uint16_t sjw;
|
||||
uint16_t sample_point;
|
||||
uint16_t prop_ts1;
|
||||
uint16_t ts2;
|
||||
#ifdef CONFIG_CAN_FD_MODE
|
||||
uint16_t sample_point_data;
|
||||
uint8_t sjw_data;
|
||||
uint8_t prop_ts1_data;
|
||||
uint8_t ts2_data;
|
||||
uint8_t tx_delay_comp_offset;
|
||||
#endif
|
||||
};
|
||||
|
||||
struct can_mcan_reg;
|
||||
|
||||
int can_mcan_set_mode(const struct can_mcan_config *cfg, enum can_mode mode);
|
||||
|
||||
int can_mcan_set_timing(const struct can_mcan_config *cfg,
|
||||
const struct can_timing *timing,
|
||||
const struct can_timing *timing_data);
|
||||
|
||||
int can_mcan_init(const struct device *dev, const struct can_mcan_config *cfg,
|
||||
struct can_mcan_msg_sram *msg_ram,
|
||||
struct can_mcan_data *data);
|
||||
|
||||
void can_mcan_line_0_isr(const struct can_mcan_config *cfg,
|
||||
struct can_mcan_msg_sram *msg_ram,
|
||||
struct can_mcan_data *data);
|
||||
|
||||
void can_mcan_line_1_isr(const struct can_mcan_config *cfg,
|
||||
struct can_mcan_msg_sram *msg_ram,
|
||||
struct can_mcan_data *data);
|
||||
|
||||
int can_mcan_recover(struct can_mcan_reg *can, k_timeout_t timeout);
|
||||
|
||||
int can_mcan_send(const struct can_mcan_config *cfg, struct can_mcan_data *data,
|
||||
struct can_mcan_msg_sram *msg_ram,
|
||||
const struct zcan_frame *frame,
|
||||
k_timeout_t timeout, can_tx_callback_t callback,
|
||||
void *callback_arg);
|
||||
|
||||
int can_mcan_attach_isr(struct can_mcan_data *data,
|
||||
struct can_mcan_msg_sram *msg_ram,
|
||||
can_rx_callback_t isr, void *cb_arg,
|
||||
const struct zcan_filter *filter);
|
||||
|
||||
void can_mcan_detach(struct can_mcan_data *data,
|
||||
struct can_mcan_msg_sram *msg_ram, int filter_nr);
|
||||
|
||||
enum can_state can_mcan_get_state(const struct can_mcan_config *cfg,
|
||||
struct can_bus_err_cnt *err_cnt);
|
||||
|
||||
#endif /* ZEPHYR_DRIVERS_CAN_MCAN_H_ */
|
1560
drivers/can/can_mcan_int.h
Normal file
1560
drivers/can/can_mcan_int.h
Normal file
File diff suppressed because it is too large
Load diff
24
drivers/can/can_stm32fd.c
Normal file
24
drivers/can/can_stm32fd.c
Normal file
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* Copyright (c) 2020 Alexander Wachter
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <drivers/can.h>
|
||||
|
||||
u32_t can_mcan_get_core_clock(struct device *dev)
|
||||
{
|
||||
u32_t core_clock = LL_RCC_GetFDCANClockFreq(LL_RCC_FDCAN_CLKSOURCE);
|
||||
u32_t dbrp, nbrp;
|
||||
|
||||
#if CONFIG_CAN_CKDIV != 0
|
||||
core_clock /= CONFIG_CAN_CKDIV * 2;
|
||||
#endif
|
||||
|
||||
__weak void can_mcan_clock_enable()
|
||||
{
|
||||
LL_RCC_SetFDCANClockSource(LL_RCC_FDCAN_CLKSOURCE_PCLK1);
|
||||
__HAL_RCC_FDCAN_CLK_ENABLE();
|
||||
|
||||
FDCAN_CONFIG->CKDIV = CONFIG_CAN_CKDIV;
|
||||
}
|
28
dts/bindings/can/bosch,m-can-base.yaml
Normal file
28
dts/bindings/can/bosch,m-can-base.yaml
Normal file
|
@ -0,0 +1,28 @@
|
|||
description: Bosch m_can CAN-FD controller base
|
||||
|
||||
compatible: "bosch,m-can-base"
|
||||
|
||||
properties:
|
||||
std-filter-elements:
|
||||
type: int
|
||||
required: true
|
||||
|
||||
ext-filter-elements:
|
||||
type: int
|
||||
required: true
|
||||
|
||||
rx-fifo0-elements:
|
||||
type: int
|
||||
required: true
|
||||
|
||||
rx-fifo1-elements:
|
||||
type: int
|
||||
required: true
|
||||
|
||||
rx-buffer-elements:
|
||||
type: int
|
||||
required: true
|
||||
|
||||
tx-buffer-elements:
|
||||
type: int
|
||||
required: true
|
12
dts/bindings/can/bosch-mcan.yaml
Normal file
12
dts/bindings/can/bosch-mcan.yaml
Normal file
|
@ -0,0 +1,12 @@
|
|||
description: Bosch m_can CAN-FD controller
|
||||
|
||||
compatible: "bosch,m-can"
|
||||
|
||||
include: can-fd-controller.yaml
|
||||
|
||||
properties:
|
||||
reg:
|
||||
required: true
|
||||
|
||||
interrupts:
|
||||
required: true
|
|
@ -30,3 +30,7 @@ properties:
|
|||
Sample point in permille for the data phase.
|
||||
This param is required if segments are not given.
|
||||
If the sample point is given, the segments are ignored.
|
||||
tx-delay-comp-offset:
|
||||
type: int
|
||||
required: false
|
||||
default: 0
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include <zephyr/types.h>
|
||||
#include <device.h>
|
||||
#include <string.h>
|
||||
#include <sys/util.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
|
@ -403,6 +404,45 @@ __subsystem struct can_driver_api {
|
|||
#endif
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Convert the DLC to the number of bytes
|
||||
*
|
||||
* This function converts a the Data Length Code to the number of bytes.
|
||||
*
|
||||
* @param dlc The Data Length Code
|
||||
*
|
||||
* @retval Number of bytes
|
||||
*/
|
||||
static inline uint8_t can_dlc_to_bytes(uint8_t dlc)
|
||||
{
|
||||
static const uint8_t dlc_table[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 12,
|
||||
16, 20, 24, 32, 48, 64};
|
||||
|
||||
return dlc > 0x0F ? 64 : dlc_table[dlc];
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Convert a number of bytes to the DLC
|
||||
*
|
||||
* This function converts a number of bytes to the Data Length Code
|
||||
*
|
||||
* @param num_bytes The number of bytes
|
||||
*
|
||||
* @retval The DLC
|
||||
*/
|
||||
|
||||
static inline uint8_t can_bytes_to_dlc(uint8_t num_bytes)
|
||||
{
|
||||
return num_bytes <= 8 ? num_bytes :
|
||||
num_bytes <= 12 ? 9 :
|
||||
num_bytes <= 16 ? 10 :
|
||||
num_bytes <= 20 ? 11 :
|
||||
num_bytes <= 24 ? 12 :
|
||||
num_bytes <= 32 ? 13 :
|
||||
num_bytes <= 48 ? 14 :
|
||||
15;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Perform data transfer to CAN bus.
|
||||
*
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue