drivers: can: Add xmc4xxx CAN support
Adds CAN drivers for XMC4xxx SoCs. XMC4xxx has multiple CAN nodes. The nodes share a common clock and a message object pool. The CAN nodes do not have a loopback mode. Instead there is an internal bus which can be used to exchange messages between nodes on the SoC. For this reason tests/samples which rely on the loopback feature have been disabled. Signed-off-by: Andriy Gelman <andriy.gelman@gmail.com>
This commit is contained in:
parent
9e9913a4fd
commit
c7dab3df08
|
@ -20,6 +20,7 @@ zephyr_library_sources_ifdef(CONFIG_CAN_STM32H7_FDCAN can_stm32h7_fdcan.c)
|
|||
zephyr_library_sources_ifdef(CONFIG_CAN_TCAN4X5X can_tcan4x5x.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_CAN_RCAR can_rcar.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_CAN_NUMAKER can_numaker.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_CAN_XMC4XXX can_xmc4xxx.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_CAN_SJA1000 can_sja1000.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_CAN_ESP32_TWAI can_esp32_twai.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_CAN_KVASER_PCI can_kvaser_pci.c)
|
||||
|
|
|
@ -108,6 +108,7 @@ source "drivers/can/Kconfig.fake"
|
|||
source "drivers/can/Kconfig.nxp_s32"
|
||||
source "drivers/can/Kconfig.tcan4x5x"
|
||||
source "drivers/can/Kconfig.mcp251xfd"
|
||||
source "drivers/can/Kconfig.xmc4xxx"
|
||||
|
||||
source "drivers/can/transceiver/Kconfig"
|
||||
|
||||
|
|
43
drivers/can/Kconfig.xmc4xxx
Normal file
43
drivers/can/Kconfig.xmc4xxx
Normal file
|
@ -0,0 +1,43 @@
|
|||
# Infineon XMC4xxx CAN configuration options
|
||||
# Copyright (c) 2023 Andriy Gelman
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
config CAN_XMC4XXX
|
||||
bool "Infineon XMC4xxx CAN Driver"
|
||||
default y
|
||||
depends on DT_HAS_INFINEON_XMC4XXX_CAN_NODE_ENABLED
|
||||
help
|
||||
Enable Infineon XMC4xxx CAN Driver
|
||||
|
||||
if CAN_XMC4XXX
|
||||
|
||||
config CAN_XMC4XXX_MAX_TX_QUEUE
|
||||
int "Maximum number of queued messages"
|
||||
default 8
|
||||
range 1 32
|
||||
help
|
||||
Defines the array size of transmit callback pointers and semaphores,
|
||||
as well as the number of messages in the TX queue.
|
||||
|
||||
config CAN_XMC4XXX_RX_FIFO_ITEMS
|
||||
int "Number of CAN messages allocated to each RX FIFO"
|
||||
default 8
|
||||
range 1 32
|
||||
help
|
||||
Defines the number of CAN messages in each RX FIFO. A separate RX FIFO
|
||||
is created for each RX filter.
|
||||
|
||||
config CAN_XMC4XXX_INTERNAL_BUS_MODE
|
||||
bool "Internal bus mode"
|
||||
help
|
||||
Connects all XMC4XXX CAN devices to an internal bus. Enables
|
||||
message exchange between MCU CAN devices without any external connectors.
|
||||
|
||||
config CAN_MAX_FILTER
|
||||
int "Maximum number of concurrent active filters"
|
||||
default 4
|
||||
range 1 32
|
||||
help
|
||||
Maximum number of filters supported by the can_add_rx_callback() API call.
|
||||
|
||||
endif # CAN_XMC4XXX
|
999
drivers/can/can_xmc4xxx.c
Normal file
999
drivers/can/can_xmc4xxx.c
Normal file
|
@ -0,0 +1,999 @@
|
|||
/*
|
||||
* Copyright (c) 2023 Andriy Gelman
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#define DT_DRV_COMPAT infineon_xmc4xxx_can_node
|
||||
|
||||
#include <zephyr/device.h>
|
||||
#include <zephyr/drivers/can.h>
|
||||
#include <zephyr/drivers/can/transceiver.h>
|
||||
#include <zephyr/drivers/pinctrl.h>
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/sys/bitarray.h>
|
||||
|
||||
#include <soc.h>
|
||||
#include <xmc_can.h>
|
||||
|
||||
#include <zephyr/logging/log.h>
|
||||
LOG_MODULE_REGISTER(can_xmc4xxx, CONFIG_CAN_LOG_LEVEL);
|
||||
|
||||
#define SP_IS_SET(inst) DT_INST_NODE_HAS_PROP(inst, sample_point) ||
|
||||
|
||||
/*
|
||||
* Macro to exclude the sample point algorithm from compilation if not used
|
||||
* Without the macro, the algorithm would always waste ROM
|
||||
*/
|
||||
#define USE_SP_ALGO (DT_INST_FOREACH_STATUS_OKAY(SP_IS_SET) 0)
|
||||
|
||||
#define CAN_XMC4XXX_MULTICAN_NODE DT_INST(0, infineon_xmc4xxx_can)
|
||||
|
||||
#define CAN_XMC4XXX_NUM_MESSAGE_OBJECTS DT_PROP(CAN_XMC4XXX_MULTICAN_NODE, message_objects)
|
||||
#define CAN_XMC4XXX_CLOCK_PRESCALER DT_PROP(CAN_XMC4XXX_MULTICAN_NODE, clock_prescaler)
|
||||
|
||||
static CAN_GLOBAL_TypeDef *const can_xmc4xxx_global_reg =
|
||||
(CAN_GLOBAL_TypeDef *)DT_REG_ADDR(CAN_XMC4XXX_MULTICAN_NODE);
|
||||
|
||||
static bool can_xmc4xxx_global_init;
|
||||
static uint32_t can_xmc4xxx_clock_frequency;
|
||||
|
||||
SYS_BITARRAY_DEFINE_STATIC(mo_usage_bitarray, CAN_XMC4XXX_NUM_MESSAGE_OBJECTS);
|
||||
static int can_xmc4xxx_num_free_mo = CAN_XMC4XXX_NUM_MESSAGE_OBJECTS;
|
||||
|
||||
#define CAN_XMC4XXX_IRQ_MIN 76
|
||||
#define CAN_XMC4XXX_MAX_DLC 8
|
||||
|
||||
#define CAN_XMC4XXX_REG_TO_NODE_IND(reg) (((uint32_t)(reg) - (uint32_t)CAN_NODE0_BASE) / 0x100)
|
||||
|
||||
struct can_xmc4xxx_tx_callback {
|
||||
can_tx_callback_t function;
|
||||
void *user_data;
|
||||
};
|
||||
|
||||
struct can_xmc4xxx_rx_callback {
|
||||
can_rx_callback_t function;
|
||||
void *user_data;
|
||||
};
|
||||
|
||||
struct can_xmc4xxx_rx_fifo {
|
||||
CAN_MO_TypeDef *base;
|
||||
CAN_MO_TypeDef *top;
|
||||
CAN_MO_TypeDef *tail;
|
||||
CAN_MO_TypeDef *head;
|
||||
};
|
||||
|
||||
struct can_xmc4xxx_data {
|
||||
struct can_driver_data common;
|
||||
|
||||
enum can_state state;
|
||||
struct k_mutex mutex;
|
||||
|
||||
struct k_sem tx_sem;
|
||||
struct can_xmc4xxx_tx_callback tx_callbacks[CONFIG_CAN_XMC4XXX_MAX_TX_QUEUE];
|
||||
|
||||
uint32_t filter_usage;
|
||||
struct can_xmc4xxx_rx_callback rx_callbacks[CONFIG_CAN_MAX_FILTER];
|
||||
struct can_xmc4xxx_rx_fifo rx_fifos[CONFIG_CAN_MAX_FILTER];
|
||||
#if defined(CONFIG_CAN_ACCEPT_RTR)
|
||||
struct can_xmc4xxx_rx_fifo rtr_fifos[CONFIG_CAN_MAX_FILTER];
|
||||
#endif
|
||||
|
||||
CAN_MO_TypeDef *tx_mo[CONFIG_CAN_XMC4XXX_MAX_TX_QUEUE];
|
||||
};
|
||||
|
||||
struct can_xmc4xxx_config {
|
||||
struct can_driver_config common;
|
||||
|
||||
CAN_NODE_TypeDef *can;
|
||||
bool clock_div8;
|
||||
|
||||
uint8_t sjw;
|
||||
uint8_t prop_seg;
|
||||
uint8_t phase_seg1;
|
||||
uint8_t phase_seg2;
|
||||
|
||||
uint8_t service_request;
|
||||
void (*irq_config_func)(void);
|
||||
|
||||
uint8_t input_src;
|
||||
const struct pinctrl_dev_config *pcfg;
|
||||
};
|
||||
|
||||
static int can_xmc4xxx_set_mode(const struct device *dev, can_mode_t mode)
|
||||
{
|
||||
struct can_xmc4xxx_data *dev_data = dev->data;
|
||||
const struct can_xmc4xxx_config *dev_cfg = dev->config;
|
||||
|
||||
if (dev_data->common.started) {
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
if ((mode & (CAN_MODE_3_SAMPLES | CAN_MODE_ONE_SHOT |
|
||||
CAN_MODE_LOOPBACK | CAN_MODE_FD)) != 0) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
if ((mode & CAN_MODE_LISTENONLY) != 0) {
|
||||
XMC_CAN_NODE_SetAnalyzerMode(dev_cfg->can);
|
||||
} else {
|
||||
XMC_CAN_NODE_ReSetAnalyzerMode(dev_cfg->can);
|
||||
}
|
||||
|
||||
dev_data->common.mode = mode;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int can_xmc4xxx_set_timing(const struct device *dev, const struct can_timing *timing)
|
||||
{
|
||||
struct can_xmc4xxx_data *dev_data = dev->data;
|
||||
const struct can_xmc4xxx_config *dev_cfg = dev->config;
|
||||
uint32_t reg;
|
||||
|
||||
if (!timing) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (dev_data->common.started) {
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
k_mutex_lock(&dev_data->mutex, K_FOREVER);
|
||||
|
||||
reg = FIELD_PREP(CAN_NODE_NBTR_DIV8_Msk, dev_cfg->clock_div8);
|
||||
reg |= FIELD_PREP(CAN_NODE_NBTR_BRP_Msk, timing->prescaler - 1);
|
||||
reg |= FIELD_PREP(CAN_NODE_NBTR_TSEG1_Msk, timing->prop_seg + timing->phase_seg1 - 1);
|
||||
reg |= FIELD_PREP(CAN_NODE_NBTR_TSEG2_Msk, timing->phase_seg2 - 1);
|
||||
reg |= FIELD_PREP(CAN_NODE_NBTR_SJW_Msk, timing->sjw - 1);
|
||||
|
||||
dev_cfg->can->NBTR = reg;
|
||||
|
||||
k_mutex_unlock(&dev_data->mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int can_xmc4xxx_send(const struct device *dev, const struct can_frame *msg,
|
||||
k_timeout_t timeout, can_tx_callback_t callback, void *callback_arg)
|
||||
{
|
||||
struct can_xmc4xxx_data *dev_data = dev->data;
|
||||
uint8_t mailbox_idx;
|
||||
struct can_xmc4xxx_tx_callback *callbacks = &dev_data->tx_callbacks[0];
|
||||
CAN_MO_TypeDef *mo;
|
||||
unsigned int key;
|
||||
|
||||
LOG_DBG("Sending %d bytes. Id: 0x%x, ID type: %s %s %s %s", can_dlc_to_bytes(msg->dlc),
|
||||
msg->id, msg->flags & CAN_FRAME_IDE ? "extended" : "standard",
|
||||
msg->flags & CAN_FRAME_RTR ? "RTR" : "",
|
||||
msg->flags & CAN_FRAME_FDF ? "FD frame" : "",
|
||||
msg->flags & CAN_FRAME_BRS ? "BRS" : "");
|
||||
|
||||
__ASSERT_NO_MSG(callback != NULL);
|
||||
|
||||
if (msg->dlc > CAN_XMC4XXX_MAX_DLC) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!dev_data->common.started) {
|
||||
return -ENETDOWN;
|
||||
}
|
||||
|
||||
if (dev_data->state == CAN_STATE_BUS_OFF) {
|
||||
return -ENETUNREACH;
|
||||
}
|
||||
|
||||
if ((msg->flags & (CAN_FRAME_FDF | CAN_FRAME_BRS)) != 0) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
if (k_sem_take(&dev_data->tx_sem, timeout) != 0) {
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
k_mutex_lock(&dev_data->mutex, K_FOREVER);
|
||||
|
||||
for (mailbox_idx = 0; mailbox_idx < CONFIG_CAN_XMC4XXX_MAX_TX_QUEUE; mailbox_idx++) {
|
||||
if (callbacks[mailbox_idx].function == NULL) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
__ASSERT_NO_MSG(mailbox_idx < CONFIG_CAN_XMC4XXX_MAX_TX_QUEUE);
|
||||
|
||||
key = irq_lock();
|
||||
/* critical section in case can_xmc4xxx_reset_tx_fifos() called in isr */
|
||||
/* so that callback function and callback_arg are consistent */
|
||||
callbacks[mailbox_idx].function = callback;
|
||||
callbacks[mailbox_idx].user_data = callback_arg;
|
||||
irq_unlock(key);
|
||||
|
||||
mo = dev_data->tx_mo[mailbox_idx];
|
||||
mo->MOCTR = CAN_MO_MOCTR_RESMSGVAL_Msk;
|
||||
|
||||
if ((msg->flags & CAN_FRAME_IDE) != 0) {
|
||||
/* MOAR - message object arbitration register */
|
||||
mo->MOAR = FIELD_PREP(CAN_MO_MOAR_PRI_Msk, 1) |
|
||||
FIELD_PREP(CAN_MO_MOAR_ID_Msk, msg->id) | CAN_MO_MOAR_IDE_Msk;
|
||||
} else {
|
||||
mo->MOAR = FIELD_PREP(CAN_MO_MOAR_PRI_Msk, 1) |
|
||||
FIELD_PREP(XMC_CAN_MO_MOAR_STDID_Msk, msg->id);
|
||||
}
|
||||
|
||||
mo->MOFCR &= ~CAN_MO_MOFCR_DLC_Msk;
|
||||
mo->MOFCR |= FIELD_PREP(CAN_MO_MOFCR_DLC_Msk, msg->dlc);
|
||||
|
||||
if ((msg->flags & CAN_FRAME_RTR) != 0) {
|
||||
mo->MOCTR = CAN_MO_MOCTR_RESDIR_Msk;
|
||||
} else {
|
||||
mo->MOCTR = CAN_MO_MOCTR_SETDIR_Msk;
|
||||
memcpy((void *)&mo->MODATAL, &msg->data[0], sizeof(uint32_t));
|
||||
memcpy((void *)&mo->MODATAH, &msg->data[4], sizeof(uint32_t));
|
||||
}
|
||||
|
||||
mo->MOCTR = CAN_MO_MOCTR_SETTXEN0_Msk | CAN_MO_MOCTR_SETTXEN1_Msk |
|
||||
CAN_MO_MOCTR_SETMSGVAL_Msk | CAN_MO_MOCTR_RESRXEN_Msk |
|
||||
CAN_MO_MOCTR_RESRTSEL_Msk;
|
||||
mo->MOCTR = CAN_MO_MOCTR_SETTXRQ_Msk;
|
||||
|
||||
k_mutex_unlock(&dev_data->mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static CAN_MO_TypeDef *can_xmc4xxx_get_mo(uint8_t *mo_index)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < CAN_XMC4XXX_NUM_MESSAGE_OBJECTS; i++) {
|
||||
int prev_val;
|
||||
|
||||
sys_bitarray_test_and_set_bit(&mo_usage_bitarray, i, &prev_val);
|
||||
if (prev_val == 0) {
|
||||
*mo_index = i;
|
||||
can_xmc4xxx_num_free_mo--;
|
||||
return &CAN_MO->MO[i];
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void can_xmc4xxx_deinit_fifo(const struct device *dev, struct can_xmc4xxx_rx_fifo *fifo)
|
||||
{
|
||||
CAN_MO_TypeDef *mo = fifo->base;
|
||||
|
||||
while (mo != NULL) {
|
||||
int next_index;
|
||||
int index;
|
||||
|
||||
/* invalidate message */
|
||||
mo->MOCTR = CAN_MO_MOCTR_RESMSGVAL_Msk;
|
||||
|
||||
next_index = FIELD_GET(CAN_MO_MOSTAT_PNEXT_Msk, mo->MOSTAT);
|
||||
index = ((uint32_t)mo - (uint32_t)&CAN_MO->MO[0]) / sizeof(*mo);
|
||||
|
||||
if ((uint32_t)mo == (uint32_t)fifo->top) {
|
||||
mo = NULL;
|
||||
} else {
|
||||
mo = &CAN_MO->MO[next_index];
|
||||
}
|
||||
|
||||
/* we need to move the node back to the list of unallocated message objects, */
|
||||
/* which is list index = 0. 255 gets rolled over to 0 in the function below */
|
||||
XMC_CAN_AllocateMOtoNodeList(can_xmc4xxx_global_reg, 255, index);
|
||||
|
||||
sys_bitarray_clear_bit(&mo_usage_bitarray, index);
|
||||
can_xmc4xxx_num_free_mo++;
|
||||
}
|
||||
}
|
||||
|
||||
static int can_xmc4xxx_init_fifo(const struct device *dev, const struct can_filter *filter,
|
||||
struct can_xmc4xxx_rx_fifo *fifo, bool is_rtr)
|
||||
{
|
||||
const struct can_xmc4xxx_config *dev_cfg = dev->config;
|
||||
CAN_MO_TypeDef *mo;
|
||||
uint32_t reg;
|
||||
uint8_t mo_index = 0, base_index;
|
||||
|
||||
if (can_xmc4xxx_num_free_mo < CONFIG_CAN_XMC4XXX_RX_FIFO_ITEMS) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
mo = can_xmc4xxx_get_mo(&mo_index);
|
||||
__ASSERT_NO_MSG(mo != NULL);
|
||||
|
||||
base_index = mo_index;
|
||||
fifo->base = mo;
|
||||
fifo->tail = mo;
|
||||
|
||||
XMC_CAN_AllocateMOtoNodeList(can_xmc4xxx_global_reg,
|
||||
CAN_XMC4XXX_REG_TO_NODE_IND(dev_cfg->can), mo_index);
|
||||
|
||||
/* setup the base object - this controls the filtering for the fifo */
|
||||
mo->MOCTR = CAN_MO_MOCTR_RESMSGVAL_Msk;
|
||||
mo->MOAMR &= ~(CAN_MO_MOAMR_AM_Msk | CAN_MO_MOAMR_MIDE_Msk);
|
||||
mo->MOAR = 0;
|
||||
|
||||
if ((filter->flags & CAN_FILTER_IDE) != 0) {
|
||||
mo->MOAMR |= FIELD_PREP(CAN_MO_MOAMR_AM_Msk, filter->mask) | CAN_MO_MOAMR_MIDE_Msk;
|
||||
mo->MOAR |= FIELD_PREP(CAN_MO_MOAR_ID_Msk, filter->id) | CAN_MO_MOAR_IDE_Msk;
|
||||
} else {
|
||||
mo->MOAMR |= FIELD_PREP(XMC_CAN_MO_MOAR_STDID_Msk, filter->mask);
|
||||
mo->MOAR |= FIELD_PREP(XMC_CAN_MO_MOAR_STDID_Msk, filter->id);
|
||||
}
|
||||
|
||||
mo->MOFCR = FIELD_PREP(CAN_MO_MOFCR_MMC_Msk, 1) | CAN_MO_MOFCR_RXIE_Msk;
|
||||
if (is_rtr) {
|
||||
mo->MOFCR |= CAN_MO_MOFCR_RMM_Msk;
|
||||
mo->MOCTR = CAN_MO_MOCTR_SETDIR_Msk;
|
||||
} else {
|
||||
mo->MOCTR = CAN_MO_MOCTR_RESDIR_Msk;
|
||||
}
|
||||
|
||||
/* Writing to MOCTR sets or resets message object properties */
|
||||
mo->MOCTR = CAN_MO_MOCTR_RESTXEN0_Msk | CAN_MO_MOCTR_RESTXEN1_Msk |
|
||||
CAN_MO_MOCTR_SETMSGVAL_Msk | CAN_MO_MOCTR_SETRXEN_Msk |
|
||||
CAN_MO_MOCTR_RESRTSEL_Msk;
|
||||
|
||||
mo->MOIPR = FIELD_PREP(CAN_MO_MOIPR_RXINP_Msk, dev_cfg->service_request);
|
||||
|
||||
/* setup the remaining message objects in the fifo */
|
||||
for (int i = 1; i < CONFIG_CAN_XMC4XXX_RX_FIFO_ITEMS; i++) {
|
||||
mo = can_xmc4xxx_get_mo(&mo_index);
|
||||
__ASSERT_NO_MSG(mo != NULL);
|
||||
|
||||
XMC_CAN_AllocateMOtoNodeList(can_xmc4xxx_global_reg,
|
||||
CAN_XMC4XXX_REG_TO_NODE_IND(dev_cfg->can), mo_index);
|
||||
|
||||
mo->MOCTR = CAN_MO_MOCTR_RESMSGVAL_Msk;
|
||||
mo->MOCTR = CAN_MO_MOCTR_SETMSGVAL_Msk | CAN_MO_MOCTR_RESRXEN_Msk;
|
||||
|
||||
/* all the other message objects in the fifo must point to the base object */
|
||||
mo->MOFGPR = FIELD_PREP(CAN_MO_MOFGPR_CUR_Msk, base_index);
|
||||
}
|
||||
|
||||
reg = 0;
|
||||
reg |= FIELD_PREP(CAN_MO_MOFGPR_CUR_Msk, base_index);
|
||||
reg |= FIELD_PREP(CAN_MO_MOFGPR_TOP_Msk, mo_index);
|
||||
reg |= FIELD_PREP(CAN_MO_MOFGPR_BOT_Msk, base_index);
|
||||
reg |= FIELD_PREP(CAN_MO_MOFGPR_SEL_Msk, base_index);
|
||||
|
||||
fifo->base->MOFGPR = reg;
|
||||
fifo->top = mo;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int can_xmc4xxx_add_rx_filter(const struct device *dev, can_rx_callback_t callback,
|
||||
void *user_data, const struct can_filter *filter)
|
||||
{
|
||||
struct can_xmc4xxx_data *dev_data = dev->data;
|
||||
int filter_idx;
|
||||
|
||||
if ((filter->flags & ~CAN_FILTER_IDE) != 0) {
|
||||
LOG_ERR("Unsupported CAN filter flags 0x%02x", filter->flags);
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
k_mutex_lock(&dev_data->mutex, K_FOREVER);
|
||||
|
||||
for (filter_idx = 0; filter_idx < CONFIG_CAN_MAX_FILTER; filter_idx++) {
|
||||
if ((BIT(filter_idx) & dev_data->filter_usage) == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (filter_idx >= CONFIG_CAN_MAX_FILTER) {
|
||||
filter_idx = -ENOSPC;
|
||||
} else {
|
||||
unsigned int key = irq_lock();
|
||||
int ret;
|
||||
|
||||
ret = can_xmc4xxx_init_fifo(dev, filter, &dev_data->rx_fifos[filter_idx], false);
|
||||
if (ret < 0) {
|
||||
irq_unlock(key);
|
||||
k_mutex_unlock(&dev_data->mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_CAN_ACCEPT_RTR)
|
||||
ret = can_xmc4xxx_init_fifo(dev, filter, &dev_data->rtr_fifos[filter_idx], true);
|
||||
if (ret < 0) {
|
||||
can_xmc4xxx_deinit_fifo(dev, &dev_data->rx_fifos[filter_idx]);
|
||||
irq_unlock(key);
|
||||
k_mutex_unlock(&dev_data->mutex);
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
dev_data->filter_usage |= BIT(filter_idx);
|
||||
dev_data->rx_callbacks[filter_idx].function = callback;
|
||||
dev_data->rx_callbacks[filter_idx].user_data = user_data;
|
||||
|
||||
irq_unlock(key);
|
||||
}
|
||||
|
||||
k_mutex_unlock(&dev_data->mutex);
|
||||
|
||||
return filter_idx;
|
||||
}
|
||||
|
||||
static void can_xmc4xxx_remove_rx_filter(const struct device *dev, int filter_idx)
|
||||
{
|
||||
struct can_xmc4xxx_data *dev_data = dev->data;
|
||||
unsigned int key;
|
||||
|
||||
if (filter_idx < 0 || filter_idx >= CONFIG_CAN_MAX_FILTER) {
|
||||
LOG_ERR("Filter ID %d out of bounds", filter_idx);
|
||||
return;
|
||||
}
|
||||
|
||||
k_mutex_lock(&dev_data->mutex, K_FOREVER);
|
||||
|
||||
if ((dev_data->filter_usage & BIT(filter_idx)) == 0) {
|
||||
k_mutex_unlock(&dev_data->mutex);
|
||||
return;
|
||||
}
|
||||
|
||||
key = irq_lock();
|
||||
can_xmc4xxx_deinit_fifo(dev, &dev_data->rx_fifos[filter_idx]);
|
||||
#if defined(CONFIG_CAN_ACCEPT_RTR)
|
||||
can_xmc4xxx_deinit_fifo(dev, &dev_data->rtr_fifos[filter_idx]);
|
||||
#endif
|
||||
|
||||
dev_data->filter_usage &= ~BIT(filter_idx);
|
||||
dev_data->rx_callbacks[filter_idx].function = NULL;
|
||||
dev_data->rx_callbacks[filter_idx].user_data = NULL;
|
||||
irq_unlock(key);
|
||||
|
||||
k_mutex_unlock(&dev_data->mutex);
|
||||
}
|
||||
|
||||
static void can_xmc4xxx_set_state_change_callback(const struct device *dev,
|
||||
can_state_change_callback_t cb, void *user_data)
|
||||
{
|
||||
struct can_xmc4xxx_data *dev_data = dev->data;
|
||||
unsigned int key;
|
||||
|
||||
key = irq_lock();
|
||||
/* critical section so that state_change_cb and state_change_cb_data are consistent */
|
||||
dev_data->common.state_change_cb = cb;
|
||||
dev_data->common.state_change_cb_user_data = user_data;
|
||||
irq_unlock(key);
|
||||
}
|
||||
|
||||
static void can_xmc4xxx_get_state_from_status(const struct device *dev, enum can_state *state,
|
||||
struct can_bus_err_cnt *err_cnt, uint32_t *status)
|
||||
{
|
||||
struct can_xmc4xxx_data *dev_data = dev->data;
|
||||
const struct can_xmc4xxx_config *dev_cfg = dev->config;
|
||||
uint8_t tec = XMC_CAN_NODE_GetTransmitErrorCounter(dev_cfg->can);
|
||||
uint8_t rec = XMC_CAN_NODE_GetTransmitErrorCounter(dev_cfg->can);
|
||||
|
||||
if (err_cnt != NULL) {
|
||||
err_cnt->tx_err_cnt = tec;
|
||||
err_cnt->rx_err_cnt = rec;
|
||||
}
|
||||
|
||||
if (state == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!dev_data->common.started) {
|
||||
*state = CAN_STATE_STOPPED;
|
||||
return;
|
||||
}
|
||||
|
||||
if ((*status & XMC_CAN_NODE_STATUS_BUS_OFF) != 0) {
|
||||
*state = CAN_STATE_BUS_OFF;
|
||||
} else if (tec >= 128 || rec >= 128) {
|
||||
*state = CAN_STATE_ERROR_PASSIVE;
|
||||
} else if ((*status & XMC_CAN_NODE_STATUS_ERROR_WARNING_STATUS) != 0) {
|
||||
*state = CAN_STATE_ERROR_WARNING;
|
||||
} else {
|
||||
*state = CAN_STATE_ERROR_ACTIVE;
|
||||
}
|
||||
}
|
||||
|
||||
static int can_xmc4xxx_get_state(const struct device *dev, enum can_state *state,
|
||||
struct can_bus_err_cnt *err_cnt)
|
||||
{
|
||||
const struct can_xmc4xxx_config *dev_cfg = dev->config;
|
||||
uint32_t status;
|
||||
|
||||
status = XMC_CAN_NODE_GetStatus(dev_cfg->can);
|
||||
|
||||
can_xmc4xxx_get_state_from_status(dev, state, err_cnt, &status);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int can_xmc4xxx_get_core_clock(const struct device *dev, uint32_t *rate)
|
||||
{
|
||||
const struct can_xmc4xxx_config *dev_cfg = dev->config;
|
||||
|
||||
*rate = can_xmc4xxx_clock_frequency;
|
||||
if (dev_cfg->clock_div8) {
|
||||
*rate /= 8;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int can_xmc4xxx_get_max_filters(const struct device *dev, bool ide)
|
||||
{
|
||||
ARG_UNUSED(ide);
|
||||
|
||||
return CONFIG_CAN_MAX_FILTER;
|
||||
}
|
||||
|
||||
#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY
|
||||
static int can_xmc4xxx_recover(const struct device *dev, k_timeout_t timeout)
|
||||
{
|
||||
struct can_xmc4xxx_data *dev_data = dev->data;
|
||||
|
||||
ARG_UNUSED(timeout);
|
||||
|
||||
if (!dev_data->common.started) {
|
||||
return -ENETDOWN;
|
||||
}
|
||||
|
||||
return -ENOTSUP;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void can_xmc4xxx_reset_tx_fifos(const struct device *dev, int status)
|
||||
{
|
||||
struct can_xmc4xxx_data *dev_data = dev->data;
|
||||
struct can_xmc4xxx_tx_callback *tx_callbacks = &dev_data->tx_callbacks[0];
|
||||
|
||||
LOG_DBG("All Tx message objects reset");
|
||||
for (int i = 0; i < CONFIG_CAN_XMC4XXX_MAX_TX_QUEUE; i++) {
|
||||
can_tx_callback_t callback;
|
||||
void *user_data;
|
||||
|
||||
callback = tx_callbacks[i].function;
|
||||
user_data = tx_callbacks[i].user_data;
|
||||
|
||||
tx_callbacks[i].function = NULL;
|
||||
|
||||
if (callback) {
|
||||
dev_data->tx_mo[i]->MOCTR = CAN_MO_MOCTR_RESMSGVAL_Msk;
|
||||
callback(dev, status, user_data);
|
||||
k_sem_give(&dev_data->tx_sem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void can_xmc4xxx_tx_handler(const struct device *dev)
|
||||
{
|
||||
struct can_xmc4xxx_data *dev_data = dev->data;
|
||||
struct can_xmc4xxx_tx_callback *tx_callbacks = &dev_data->tx_callbacks[0];
|
||||
|
||||
for (int i = 0; i < CONFIG_CAN_XMC4XXX_MAX_TX_QUEUE; i++) {
|
||||
CAN_MO_TypeDef *mo = dev_data->tx_mo[i];
|
||||
|
||||
if ((mo->MOSTAT & XMC_CAN_MO_STATUS_TX_PENDING) != 0) {
|
||||
can_tx_callback_t callback;
|
||||
void *user_data;
|
||||
|
||||
mo->MOCTR = XMC_CAN_MO_RESET_STATUS_TX_PENDING;
|
||||
|
||||
callback = tx_callbacks[i].function;
|
||||
user_data = tx_callbacks[i].user_data;
|
||||
|
||||
tx_callbacks[i].function = NULL;
|
||||
|
||||
if (callback) {
|
||||
callback(dev, 0, user_data);
|
||||
k_sem_give(&dev_data->tx_sem);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void can_xmc4xxx_increment_fifo_tail(struct can_xmc4xxx_rx_fifo *fifo)
|
||||
{
|
||||
uint8_t next_index;
|
||||
|
||||
if ((uint32_t)fifo->tail == (uint32_t)fifo->top) {
|
||||
fifo->tail = fifo->base;
|
||||
return;
|
||||
}
|
||||
|
||||
next_index = FIELD_GET(CAN_MO_MOSTAT_PNEXT_Msk, fifo->tail->MOSTAT);
|
||||
fifo->tail = &CAN_MO->MO[next_index];
|
||||
}
|
||||
|
||||
static inline bool can_xmc4xxx_is_fifo_empty(struct can_xmc4xxx_rx_fifo *fifo)
|
||||
{
|
||||
if (fifo->tail->MOSTAT & XMC_CAN_MO_STATUS_RX_PENDING) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline void can_xmc4xxx_update_fifo_head(struct can_xmc4xxx_rx_fifo *fifo)
|
||||
{
|
||||
uint32_t reg = fifo->base->MOFGPR;
|
||||
uint8_t top_index, bot_index, cur_index;
|
||||
uint8_t head_index = FIELD_GET(CAN_MO_MOFGPR_CUR_Msk, reg);
|
||||
|
||||
fifo->head = &CAN_MO->MO[head_index];
|
||||
top_index = FIELD_GET(CAN_MO_MOFGPR_TOP_Msk, reg);
|
||||
bot_index = FIELD_GET(CAN_MO_MOFGPR_BOT_Msk, reg);
|
||||
cur_index = FIELD_GET(CAN_MO_MOFGPR_CUR_Msk, reg);
|
||||
|
||||
LOG_DBG("Fifo: top %d, bot %d, cur %d", top_index, bot_index, cur_index);
|
||||
}
|
||||
|
||||
static void can_xmc4xxx_rx_fifo_handler(const struct device *dev, struct can_xmc4xxx_rx_fifo *fifo,
|
||||
struct can_xmc4xxx_rx_callback *rx_callback)
|
||||
{
|
||||
bool is_rtr = (fifo->base->MOSTAT & CAN_MO_MOSTAT_DIR_Msk) != 0;
|
||||
|
||||
while (!can_xmc4xxx_is_fifo_empty(fifo)) {
|
||||
struct can_frame frame;
|
||||
CAN_MO_TypeDef *mo_tail = fifo->tail;
|
||||
|
||||
memset(&frame, 0, sizeof(frame));
|
||||
|
||||
if ((mo_tail->MOAR & CAN_MO_MOAR_IDE_Msk) != 0) {
|
||||
frame.flags |= CAN_FRAME_IDE;
|
||||
frame.id = FIELD_GET(CAN_MO_MOAR_ID_Msk, mo_tail->MOAR);
|
||||
} else {
|
||||
frame.id = FIELD_GET(XMC_CAN_MO_MOAR_STDID_Msk, mo_tail->MOAR);
|
||||
}
|
||||
|
||||
frame.dlc = FIELD_GET(CAN_MO_MOFCR_DLC_Msk, mo_tail->MOFCR);
|
||||
|
||||
if (!is_rtr) {
|
||||
memcpy(&frame.data[0], (void *)&mo_tail->MODATAL, sizeof(uint32_t));
|
||||
memcpy(&frame.data[4], (void *)&mo_tail->MODATAH, sizeof(uint32_t));
|
||||
} else {
|
||||
frame.flags |= CAN_FRAME_RTR;
|
||||
memset(&frame.data[0], 0, CAN_MAX_DLEN);
|
||||
}
|
||||
|
||||
if (rx_callback->function != NULL) {
|
||||
rx_callback->function(dev, &frame, rx_callback->user_data);
|
||||
}
|
||||
|
||||
/* reset the rx pending bit on the tail */
|
||||
mo_tail->MOCTR = XMC_CAN_MO_RESET_STATUS_RX_PENDING;
|
||||
can_xmc4xxx_increment_fifo_tail(fifo);
|
||||
}
|
||||
}
|
||||
|
||||
static void can_xmc4xxx_rx_handler(const struct device *dev)
|
||||
{
|
||||
struct can_xmc4xxx_data *dev_data = dev->data;
|
||||
|
||||
for (int i = 0; i < CONFIG_CAN_MAX_FILTER; i++) {
|
||||
if ((BIT(i) & dev_data->filter_usage) == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
can_xmc4xxx_update_fifo_head(&dev_data->rx_fifos[i]);
|
||||
can_xmc4xxx_rx_fifo_handler(dev, &dev_data->rx_fifos[i],
|
||||
&dev_data->rx_callbacks[i]);
|
||||
#if defined(CONFIG_CAN_ACCEPT_RTR)
|
||||
can_xmc4xxx_update_fifo_head(&dev_data->rtr_fifos[i]);
|
||||
can_xmc4xxx_rx_fifo_handler(dev, &dev_data->rtr_fifos[i],
|
||||
&dev_data->rx_callbacks[i]);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
static void can_xmc4xxx_state_change_handler(const struct device *dev, uint32_t status)
|
||||
{
|
||||
const struct can_xmc4xxx_config *dev_cfg = dev->config;
|
||||
struct can_xmc4xxx_data *dev_data = dev->data;
|
||||
enum can_state new_state;
|
||||
struct can_bus_err_cnt err_cnt;
|
||||
|
||||
can_xmc4xxx_get_state_from_status(dev, &new_state, &err_cnt, &status);
|
||||
if (dev_data->state != new_state) {
|
||||
if (dev_data->common.state_change_cb) {
|
||||
dev_data->common.state_change_cb(
|
||||
dev, new_state, err_cnt,
|
||||
dev_data->common.state_change_cb_user_data);
|
||||
}
|
||||
|
||||
if (dev_data->state != CAN_STATE_STOPPED && new_state == CAN_STATE_BUS_OFF) {
|
||||
/* re-enable the node after auto bus-off recovery completes */
|
||||
XMC_CAN_NODE_ResetInitBit(dev_cfg->can);
|
||||
}
|
||||
|
||||
dev_data->state = new_state;
|
||||
|
||||
if (dev_data->state == CAN_STATE_BUS_OFF) {
|
||||
can_xmc4xxx_reset_tx_fifos(dev, -ENETDOWN);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void can_xmc4xxx_isr(const struct device *dev)
|
||||
{
|
||||
const struct can_xmc4xxx_config *dev_cfg = dev->config;
|
||||
uint32_t status;
|
||||
|
||||
status = XMC_CAN_NODE_GetStatus(dev_cfg->can);
|
||||
XMC_CAN_NODE_ClearStatus(dev_cfg->can, status);
|
||||
|
||||
if ((status & XMC_CAN_NODE_STATUS_TX_OK) != 0) {
|
||||
can_xmc4xxx_tx_handler(dev);
|
||||
}
|
||||
|
||||
if ((status & XMC_CAN_NODE_STATUS_RX_OK) != 0) {
|
||||
can_xmc4xxx_rx_handler(dev);
|
||||
}
|
||||
|
||||
if ((status & XMC_CAN_NODE_STATUS_ALERT_WARNING) != 0) {
|
||||
/* change of bit NSRx.BOFF */
|
||||
/* change of bit NSRx.EWRN */
|
||||
can_xmc4xxx_state_change_handler(dev, status);
|
||||
}
|
||||
}
|
||||
|
||||
static int can_xmc4xxx_get_capabilities(const struct device *dev, can_mode_t *cap)
|
||||
{
|
||||
ARG_UNUSED(dev);
|
||||
|
||||
*cap = CAN_MODE_NORMAL | CAN_MODE_LISTENONLY;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int can_xmc4xxx_start(const struct device *dev)
|
||||
{
|
||||
struct can_xmc4xxx_data *dev_data = dev->data;
|
||||
const struct can_xmc4xxx_config *dev_cfg = dev->config;
|
||||
int ret = 0;
|
||||
unsigned int key;
|
||||
|
||||
if (dev_data->common.started) {
|
||||
return -EALREADY;
|
||||
}
|
||||
|
||||
key = irq_lock();
|
||||
can_xmc4xxx_reset_tx_fifos(dev, -ENETDOWN);
|
||||
irq_unlock(key);
|
||||
|
||||
if (dev_cfg->common.phy != NULL) {
|
||||
ret = can_transceiver_enable(dev_cfg->common.phy, dev_data->common.mode);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Failed to enable CAN transceiver [%d]", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
k_mutex_lock(&dev_data->mutex, K_FOREVER);
|
||||
|
||||
XMC_CAN_NODE_DisableConfigurationChange(dev_cfg->can);
|
||||
|
||||
dev_data->common.started = true;
|
||||
XMC_CAN_NODE_ResetInitBit(dev_cfg->can);
|
||||
|
||||
k_mutex_unlock(&dev_data->mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int can_xmc4xxx_stop(const struct device *dev)
|
||||
{
|
||||
struct can_xmc4xxx_data *dev_data = dev->data;
|
||||
const struct can_xmc4xxx_config *dev_cfg = dev->config;
|
||||
int ret = 0;
|
||||
unsigned int key;
|
||||
|
||||
if (!dev_data->common.started) {
|
||||
return -EALREADY;
|
||||
}
|
||||
|
||||
key = irq_lock();
|
||||
XMC_CAN_NODE_SetInitBit(dev_cfg->can);
|
||||
|
||||
XMC_CAN_NODE_EnableConfigurationChange(dev_cfg->can);
|
||||
|
||||
can_xmc4xxx_reset_tx_fifos(dev, -ENETDOWN);
|
||||
dev_data->common.started = false;
|
||||
irq_unlock(key);
|
||||
|
||||
if (dev_cfg->common.phy != NULL) {
|
||||
ret = can_transceiver_disable(dev_cfg->common.phy);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Failed to disable CAN transceiver [%d]", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int can_xmc4xxx_init_timing_struct(struct can_timing *timing, const struct device *dev)
|
||||
{
|
||||
int ret;
|
||||
const struct can_xmc4xxx_config *dev_cfg = dev->config;
|
||||
|
||||
if (USE_SP_ALGO && dev_cfg->common.sample_point > 0) {
|
||||
ret = can_calc_timing(dev, timing, dev_cfg->common.bus_speed,
|
||||
dev_cfg->common.sample_point);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
LOG_DBG("Presc: %d, BS1: %d, BS2: %d", timing->prescaler, timing->phase_seg1,
|
||||
timing->phase_seg2);
|
||||
LOG_DBG("Sample-point err : %d", ret);
|
||||
} else {
|
||||
timing->sjw = dev_cfg->sjw;
|
||||
timing->prop_seg = dev_cfg->prop_seg;
|
||||
timing->phase_seg1 = dev_cfg->phase_seg1;
|
||||
timing->phase_seg2 = dev_cfg->phase_seg2;
|
||||
ret = can_calc_prescaler(dev, timing, dev_cfg->common.bus_speed);
|
||||
if (ret > 0) {
|
||||
LOG_WRN("Bitrate error: %d", ret);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int can_xmc4xxx_init(const struct device *dev)
|
||||
{
|
||||
struct can_xmc4xxx_data *dev_data = dev->data;
|
||||
const struct can_xmc4xxx_config *dev_cfg = dev->config;
|
||||
int ret;
|
||||
struct can_timing timing = {0};
|
||||
CAN_MO_TypeDef *mo;
|
||||
uint8_t mo_index = 0;
|
||||
|
||||
k_sem_init(&dev_data->tx_sem, CONFIG_CAN_XMC4XXX_MAX_TX_QUEUE,
|
||||
CONFIG_CAN_XMC4XXX_MAX_TX_QUEUE);
|
||||
k_mutex_init(&dev_data->mutex);
|
||||
|
||||
if (!can_xmc4xxx_global_init) {
|
||||
uint32_t fdr_step;
|
||||
uint32_t clk_module;
|
||||
|
||||
XMC_CAN_Enable(can_xmc4xxx_global_reg);
|
||||
XMC_CAN_SetBaudrateClockSource(can_xmc4xxx_global_reg, XMC_CAN_CANCLKSRC_FPERI);
|
||||
|
||||
clk_module = XMC_CAN_GetBaudrateClockFrequency(can_xmc4xxx_global_reg);
|
||||
fdr_step = 1024 - CAN_XMC4XXX_CLOCK_PRESCALER;
|
||||
can_xmc4xxx_clock_frequency = clk_module / CAN_XMC4XXX_CLOCK_PRESCALER;
|
||||
|
||||
LOG_DBG("Clock frequency %dHz\n", can_xmc4xxx_clock_frequency);
|
||||
|
||||
can_xmc4xxx_global_reg->FDR &= ~(CAN_FDR_DM_Msk | CAN_FDR_STEP_Msk);
|
||||
can_xmc4xxx_global_reg->FDR |= FIELD_PREP(CAN_FDR_DM_Msk, XMC_CAN_DM_NORMAL) |
|
||||
FIELD_PREP(CAN_FDR_STEP_Msk, fdr_step);
|
||||
|
||||
can_xmc4xxx_global_init = true;
|
||||
}
|
||||
|
||||
XMC_CAN_NODE_EnableConfigurationChange(dev_cfg->can);
|
||||
|
||||
XMC_CAN_NODE_SetReceiveInput(dev_cfg->can, dev_cfg->input_src);
|
||||
|
||||
XMC_CAN_NODE_SetInitBit(dev_cfg->can);
|
||||
|
||||
XMC_CAN_NODE_SetEventNodePointer(dev_cfg->can, XMC_CAN_NODE_POINTER_EVENT_ALERT,
|
||||
dev_cfg->service_request);
|
||||
|
||||
XMC_CAN_NODE_SetEventNodePointer(dev_cfg->can, XMC_CAN_NODE_POINTER_EVENT_LEC,
|
||||
dev_cfg->service_request);
|
||||
|
||||
XMC_CAN_NODE_SetEventNodePointer(dev_cfg->can, XMC_CAN_NODE_POINTER_EVENT_TRANSFER_OK,
|
||||
dev_cfg->service_request);
|
||||
|
||||
XMC_CAN_NODE_SetEventNodePointer(dev_cfg->can, XMC_CAN_NODE_POINTER_EVENT_FRAME_COUNTER,
|
||||
dev_cfg->service_request);
|
||||
|
||||
XMC_CAN_NODE_EnableEvent(dev_cfg->can, XMC_CAN_NODE_EVENT_TX_INT |
|
||||
XMC_CAN_NODE_EVENT_ALERT);
|
||||
|
||||
/* set up tx messages */
|
||||
for (int i = 0; i < CONFIG_CAN_XMC4XXX_MAX_TX_QUEUE; i++) {
|
||||
mo = can_xmc4xxx_get_mo(&mo_index);
|
||||
if (mo == NULL) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
dev_data->tx_mo[i] = mo;
|
||||
|
||||
XMC_CAN_AllocateMOtoNodeList(can_xmc4xxx_global_reg,
|
||||
CAN_XMC4XXX_REG_TO_NODE_IND(dev_cfg->can), mo_index);
|
||||
|
||||
mo->MOIPR = FIELD_PREP(CAN_MO_MOIPR_TXINP_Msk, dev_cfg->service_request);
|
||||
mo->MOFCR = FIELD_PREP(CAN_MO_MOFCR_MMC_Msk, 0) | CAN_MO_MOFCR_TXIE_Msk;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_CAN_XMC4XXX_INTERNAL_BUS_MODE
|
||||
/* The name of this function is misleading. It doesn't actually enable */
|
||||
/* loopback on a single node, but connects all CAN devices to an internal bus. */
|
||||
XMC_CAN_NODE_EnableLoopBack(dev_cfg->can);
|
||||
#endif
|
||||
|
||||
dev_cfg->irq_config_func();
|
||||
|
||||
dev_data->state = CAN_STATE_STOPPED;
|
||||
|
||||
#ifndef CONFIG_CAN_XMC4XXX_INTERNAL_BUS_MODE
|
||||
ret = pinctrl_apply_state(dev_cfg->pcfg, PINCTRL_STATE_DEFAULT);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
ret = can_xmc4xxx_init_timing_struct(&timing, dev);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
return can_set_timing(dev, &timing);
|
||||
}
|
||||
|
||||
static const struct can_driver_api can_xmc4xxx_api_funcs = {
|
||||
.get_capabilities = can_xmc4xxx_get_capabilities,
|
||||
.set_mode = can_xmc4xxx_set_mode,
|
||||
.set_timing = can_xmc4xxx_set_timing,
|
||||
.start = can_xmc4xxx_start,
|
||||
.stop = can_xmc4xxx_stop,
|
||||
.send = can_xmc4xxx_send,
|
||||
.add_rx_filter = can_xmc4xxx_add_rx_filter,
|
||||
.remove_rx_filter = can_xmc4xxx_remove_rx_filter,
|
||||
#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY
|
||||
.recover = can_xmc4xxx_recover,
|
||||
#endif
|
||||
.get_state = can_xmc4xxx_get_state,
|
||||
.set_state_change_callback = can_xmc4xxx_set_state_change_callback,
|
||||
.get_core_clock = can_xmc4xxx_get_core_clock,
|
||||
.get_max_filters = can_xmc4xxx_get_max_filters,
|
||||
.timing_min = {
|
||||
.sjw = 1,
|
||||
.prop_seg = 0,
|
||||
.phase_seg1 = 3,
|
||||
.phase_seg2 = 2,
|
||||
.prescaler = 1,
|
||||
},
|
||||
.timing_max = {
|
||||
.sjw = 4,
|
||||
.prop_seg = 0,
|
||||
.phase_seg1 = 16,
|
||||
.phase_seg2 = 8,
|
||||
.prescaler = 64,
|
||||
},
|
||||
};
|
||||
|
||||
#define CAN_XMC4XXX_INIT(inst) \
|
||||
static void can_xmc4xxx_irq_config_##inst(void) \
|
||||
{ \
|
||||
IRQ_CONNECT(DT_INST_IRQN(inst), DT_INST_IRQ(inst, priority), can_xmc4xxx_isr, \
|
||||
DEVICE_DT_INST_GET(inst), 0); \
|
||||
irq_enable(DT_INST_IRQN(inst)); \
|
||||
} \
|
||||
\
|
||||
PINCTRL_DT_INST_DEFINE(inst); \
|
||||
\
|
||||
static struct can_xmc4xxx_data can_xmc4xxx_data_##inst; \
|
||||
static const struct can_xmc4xxx_config can_xmc4xxx_config_##inst = { \
|
||||
.common = CAN_DT_DRIVER_CONFIG_INST_GET(inst, 1000000), \
|
||||
.can = (CAN_NODE_TypeDef *)DT_INST_REG_ADDR(inst), \
|
||||
.clock_div8 = DT_INST_PROP(inst, clock_div8), \
|
||||
.sjw = DT_INST_PROP(inst, sjw), \
|
||||
.prop_seg = DT_INST_PROP_OR(inst, prop_seg, 0), \
|
||||
.phase_seg1 = DT_INST_PROP_OR(inst, phase_seg1, 0), \
|
||||
.phase_seg2 = DT_INST_PROP_OR(inst, phase_seg2, 0), \
|
||||
.irq_config_func = can_xmc4xxx_irq_config_##inst, \
|
||||
.service_request = DT_INST_IRQN(inst) - CAN_XMC4XXX_IRQ_MIN, \
|
||||
.input_src = DT_INST_ENUM_IDX(inst, input_src), \
|
||||
.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(inst), \
|
||||
}; \
|
||||
\
|
||||
CAN_DEVICE_DT_INST_DEFINE(inst, can_xmc4xxx_init, NULL, &can_xmc4xxx_data_##inst, \
|
||||
&can_xmc4xxx_config_##inst, POST_KERNEL, \
|
||||
CONFIG_CAN_INIT_PRIORITY, &can_xmc4xxx_api_funcs);
|
||||
|
||||
DT_INST_FOREACH_STATUS_OKAY(CAN_XMC4XXX_INIT)
|
|
@ -573,4 +573,45 @@
|
|||
/omit-if-no-ref/ eth_p6_6_clk_tx: eth_p6_6_clk_tx {
|
||||
pinmux = <XMC4XXX_PINMUX_SET(6, 6, 0)>;
|
||||
};
|
||||
|
||||
/omit-if-no-ref/ can_tx_p0_0_node0: can_tx_p0_0_node0 {
|
||||
pinmux = <XMC4XXX_PINMUX_SET(0, 0, 2)>;
|
||||
};
|
||||
/omit-if-no-ref/ can_tx_p1_4_node0: can_tx_p1_4_node0 {
|
||||
pinmux = <XMC4XXX_PINMUX_SET(1, 4, 2)>;
|
||||
};
|
||||
/omit-if-no-ref/ can_tx_p1_5_node1: can_tx_p1_5_node1 {
|
||||
pinmux = <XMC4XXX_PINMUX_SET(1, 5, 1)>;
|
||||
};
|
||||
/omit-if-no-ref/ can_tx_p1_9_node2: can_tx_p1_9_node2 {
|
||||
pinmux = <XMC4XXX_PINMUX_SET(1, 9, 2)>;
|
||||
};
|
||||
/omit-if-no-ref/ can_tx_p1_12_node1: can_tx_p1_12_node1 {
|
||||
pinmux = <XMC4XXX_PINMUX_SET(1, 12, 2)>;
|
||||
};
|
||||
/omit-if-no-ref/ can_tx_p2_7_node1: can_tx_p2_7_node1 {
|
||||
pinmux = <XMC4XXX_PINMUX_SET(2, 7, 2)>;
|
||||
};
|
||||
/omit-if-no-ref/ can_tx_p3_2_node0: can_tx_p3_2_node0 {
|
||||
pinmux = <XMC4XXX_PINMUX_SET(3, 2, 2)>;
|
||||
};
|
||||
|
||||
/omit-if-no-ref/ can_rx_p1_5_node0: can_rx_p1_5_node0 {
|
||||
pinmux = <XMC4XXX_PINMUX_SET(1, 5, 0)>; /* CAN input src = RXDA */
|
||||
};
|
||||
/omit-if-no-ref/ can_rx_p14_3_node0: can_rx_p14_3_node0 {
|
||||
pinmux = <XMC4XXX_PINMUX_SET(14, 3, 0)>; /* CAN input src = RXDB */
|
||||
};
|
||||
/omit-if-no-ref/ can_rx_p2_6_node1: can_rx_p2_6_node1 {
|
||||
pinmux = <XMC4XXX_PINMUX_SET(2, 6, 0)>; /* CAN input src = RXDA */
|
||||
};
|
||||
/omit-if-no-ref/ can_rx_p1_13_node1: can_rx_p1_13_node1 {
|
||||
pinmux = <XMC4XXX_PINMUX_SET(1, 13, 0)>; /* CAN input src = RXDC */
|
||||
};
|
||||
/omit-if-no-ref/ can_rx_p1_4_node1: can_rx_p1_4_node1 {
|
||||
pinmux = <XMC4XXX_PINMUX_SET(1, 4, 0)>; /* CAN input src = RXDD */
|
||||
};
|
||||
/omit-if-no-ref/ can_rx_p1_8_node2: can_rx_p1_8_node2 {
|
||||
pinmux = <XMC4XXX_PINMUX_SET(1, 8, 0)>; /* CAN input src = RXDA */
|
||||
};
|
||||
};
|
||||
|
|
|
@ -93,3 +93,7 @@
|
|||
status = "disabled";
|
||||
};
|
||||
};
|
||||
|
||||
&can {
|
||||
message-objects = <64>;
|
||||
};
|
||||
|
|
|
@ -1160,4 +1160,108 @@
|
|||
/omit-if-no-ref/ eth_p6_6_clk_tx: eth_p6_6_clk_tx {
|
||||
pinmux = <XMC4XXX_PINMUX_SET(6, 6, 0)>;
|
||||
};
|
||||
|
||||
/omit-if-no-ref/ can_tx_p0_0_node0: can_tx_p0_0_node0 {
|
||||
pinmux = <XMC4XXX_PINMUX_SET(0, 0, 2)>;
|
||||
};
|
||||
/omit-if-no-ref/ can_tx_p1_4_node0: can_tx_p1_4_node0 {
|
||||
pinmux = <XMC4XXX_PINMUX_SET(1, 4, 2)>;
|
||||
};
|
||||
/omit-if-no-ref/ can_tx_p1_5_node1: can_tx_p1_5_node1 {
|
||||
pinmux = <XMC4XXX_PINMUX_SET(1, 5, 1)>;
|
||||
};
|
||||
/omit-if-no-ref/ can_tx_p1_9_node2: can_tx_p1_9_node2 {
|
||||
pinmux = <XMC4XXX_PINMUX_SET(1, 9, 2)>;
|
||||
};
|
||||
/omit-if-no-ref/ can_tx_p1_12_node1: can_tx_p1_12_node1 {
|
||||
pinmux = <XMC4XXX_PINMUX_SET(1, 12, 2)>;
|
||||
};
|
||||
/omit-if-no-ref/ can_tx_p2_0_node0: can_tx_p2_0_node0 {
|
||||
pinmux = <XMC4XXX_PINMUX_SET(2, 0, 1)>;
|
||||
};
|
||||
/omit-if-no-ref/ can_tx_p2_1_node5: can_tx_p2_1_node5 {
|
||||
pinmux = <XMC4XXX_PINMUX_SET(2, 1, 1)>;
|
||||
};
|
||||
/omit-if-no-ref/ can_tx_p2_7_node1: can_tx_p2_7_node1 {
|
||||
pinmux = <XMC4XXX_PINMUX_SET(2, 7, 2)>;
|
||||
};
|
||||
/omit-if-no-ref/ can_tx_p2_14_node4: can_tx_p2_14_node4 {
|
||||
pinmux = <XMC4XXX_PINMUX_SET(2, 14, 4)>;
|
||||
};
|
||||
/omit-if-no-ref/ can_tx_p3_2_node0: can_tx_p3_2_node0 {
|
||||
pinmux = <XMC4XXX_PINMUX_SET(3, 2, 2)>;
|
||||
};
|
||||
/omit-if-no-ref/ can_tx_p3_7_node2: can_tx_p3_7_node2 {
|
||||
pinmux = <XMC4XXX_PINMUX_SET(3, 7, 2)>;
|
||||
};
|
||||
/omit-if-no-ref/ can_tx_p3_9_node1: can_tx_p3_9_node1 {
|
||||
pinmux = <XMC4XXX_PINMUX_SET(3, 9, 2)>;
|
||||
};
|
||||
/omit-if-no-ref/ can_tx_p3_10_node0: can_tx_p3_10_node0 {
|
||||
pinmux = <XMC4XXX_PINMUX_SET(3, 10, 2)>;
|
||||
};
|
||||
/omit-if-no-ref/ can_tx_p4_0_node3: can_tx_p4_0_node3 {
|
||||
pinmux = <XMC4XXX_PINMUX_SET(4, 0, 1)>;
|
||||
};
|
||||
/omit-if-no-ref/ can_tx_p4_7_node2: can_tx_p4_7_node2 {
|
||||
pinmux = <XMC4XXX_PINMUX_SET(4, 7, 2)>;
|
||||
};
|
||||
/omit-if-no-ref/ can_tx_p5_8_node4: can_tx_p5_8_node4 {
|
||||
pinmux = <XMC4XXX_PINMUX_SET(5, 8, 4)>;
|
||||
};
|
||||
/omit-if-no-ref/ can_tx_p5_11_node5: can_tx_p5_11_node5 {
|
||||
pinmux = <XMC4XXX_PINMUX_SET(5, 11, 4)>;
|
||||
};
|
||||
/omit-if-no-ref/ can_tx_p6_5_node3: can_tx_p6_5_node3 {
|
||||
pinmux = <XMC4XXX_PINMUX_SET(6, 5, 1)>;
|
||||
};
|
||||
|
||||
/omit-if-no-ref/ can_rx_p1_5_node0: can_rx_p1_5_node0 {
|
||||
pinmux = <XMC4XXX_PINMUX_SET(1, 5, 0)>; /* CAN input src = RXDA */
|
||||
};
|
||||
/omit-if-no-ref/ can_rx_p14_3_node0: can_rx_p14_3_node0 {
|
||||
pinmux = <XMC4XXX_PINMUX_SET(14, 3, 0)>; /* CAN input src = RXDB */
|
||||
};
|
||||
/omit-if-no-ref/ can_rx_p3_12_node0: can_rx_p3_12_node0 {
|
||||
pinmux = <XMC4XXX_PINMUX_SET(3, 12, 0)>; /* CAN input src = RXDC */
|
||||
};
|
||||
/omit-if-no-ref/ can_rx_p2_6_node1: can_rx_p2_6_node1 {
|
||||
pinmux = <XMC4XXX_PINMUX_SET(2, 6, 0)>; /* CAN input src = RXDA */
|
||||
};
|
||||
/omit-if-no-ref/ can_rx_p3_11_node1: can_rx_p3_11_node1 {
|
||||
pinmux = <XMC4XXX_PINMUX_SET(3, 11, 0)>; /* CAN input src = RXDB */
|
||||
};
|
||||
/omit-if-no-ref/ can_rx_p1_13_node1: can_rx_p1_13_node1 {
|
||||
pinmux = <XMC4XXX_PINMUX_SET(1, 13, 0)>; /* CAN input src = RXDC */
|
||||
};
|
||||
/omit-if-no-ref/ can_rx_p1_4_node1: can_rx_p1_4_node1 {
|
||||
pinmux = <XMC4XXX_PINMUX_SET(1, 4, 0)>; /* CAN input src = RXDD */
|
||||
};
|
||||
/omit-if-no-ref/ can_rx_p1_8_node2: can_rx_p1_8_node2 {
|
||||
pinmux = <XMC4XXX_PINMUX_SET(1, 8, 0)>; /* CAN input src = RXDA */
|
||||
};
|
||||
/omit-if-no-ref/ can_rx_p3_8_node2: can_rx_p3_8_node2 {
|
||||
pinmux = <XMC4XXX_PINMUX_SET(3, 8, 0)>; /* CAN input src = RXDB */
|
||||
};
|
||||
/omit-if-no-ref/ can_rx_p4_6_node2: can_rx_p4_6_node2 {
|
||||
pinmux = <XMC4XXX_PINMUX_SET(4, 6, 0)>; /* CAN input src = RXDC */
|
||||
};
|
||||
/omit-if-no-ref/ can_rx_p0_8_node3: can_rx_p0_8_node3 {
|
||||
pinmux = <XMC4XXX_PINMUX_SET(0, 8, 0)>; /* CAN input src = RXDA */
|
||||
};
|
||||
/omit-if-no-ref/ can_rx_p6_6_node3: can_rx_p6_6_node3 {
|
||||
pinmux = <XMC4XXX_PINMUX_SET(6, 6, 0)>; /* CAN input src = RXDB */
|
||||
};
|
||||
/omit-if-no-ref/ can_rx_p2_15_node4: can_rx_p2_15_node4 {
|
||||
pinmux = <XMC4XXX_PINMUX_SET(2, 15, 0)>; /* CAN input src = RXDA */
|
||||
};
|
||||
/omit-if-no-ref/ can_rx_p14_4_node4: can_rx_p14_4_node4 {
|
||||
pinmux = <XMC4XXX_PINMUX_SET(14, 4, 0)>; /* CAN input src = RXDB */
|
||||
};
|
||||
/omit-if-no-ref/ can_rx_p5_10_node5: can_rx_p5_10_node5 {
|
||||
pinmux = <XMC4XXX_PINMUX_SET(5, 10, 0)>; /* CAN input src = RXDA */
|
||||
};
|
||||
/omit-if-no-ref/ can_rx_p2_6_node5: can_rx_p2_6_node5 {
|
||||
pinmux = <XMC4XXX_PINMUX_SET(2, 6, 0)>; /* CAN input src = RXDB */
|
||||
};
|
||||
};
|
||||
|
|
|
@ -96,3 +96,27 @@
|
|||
status = "disabled";
|
||||
};
|
||||
};
|
||||
|
||||
&can {
|
||||
message-objects = <256>;
|
||||
can_node3: can_node3@48014500 {
|
||||
compatible = "infineon,xmc4xxx-can-node";
|
||||
reg = <0x48014500 0x100>;
|
||||
interrupts = <79 1>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
can_node4: can_node4@48014600 {
|
||||
compatible = "infineon,xmc4xxx-can-node";
|
||||
reg = <0x48014600 0x100>;
|
||||
interrupts = <80 1>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
can_node5: can_node5@48014700 {
|
||||
compatible = "infineon,xmc4xxx-can-node";
|
||||
reg = <0x48014700 0x100>;
|
||||
interrupts = <81 1>;
|
||||
status = "disabled";
|
||||
};
|
||||
};
|
||||
|
|
|
@ -253,6 +253,36 @@
|
|||
#size-cells = <0>;
|
||||
};
|
||||
};
|
||||
|
||||
can: can@48014000 {
|
||||
compatible = "infineon,xmc4xxx-can";
|
||||
reg = <0x48014000 0x4000>;
|
||||
clock-prescaler = <1>;
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
|
||||
can_node0: can_node0@48014200 {
|
||||
compatible = "infineon,xmc4xxx-can-node";
|
||||
reg = <0x48014200 0x100>;
|
||||
interrupts = <76 1>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
can_node1: can_node1@48014300 {
|
||||
compatible = "infineon,xmc4xxx-can-node";
|
||||
reg = <0x48014300 0x100>;
|
||||
interrupts = <77 1>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
can_node2: can_node2@48014400 {
|
||||
compatible = "infineon,xmc4xxx-can-node";
|
||||
reg = <0x48014400 0x100>;
|
||||
interrupts = <78 1>;
|
||||
status = "disabled";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
|
|
35
dts/bindings/can/infineon,xmc4xxx-can-node.yaml
Normal file
35
dts/bindings/can/infineon,xmc4xxx-can-node.yaml
Normal file
|
@ -0,0 +1,35 @@
|
|||
# Copyright (c) 2023 Andriy Gelman
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
description: |
|
||||
Infineon XMC4xxx CAN Node
|
||||
|
||||
compatible: "infineon,xmc4xxx-can-node"
|
||||
|
||||
include: ["can-controller.yaml", "pinctrl-device.yaml"]
|
||||
|
||||
properties:
|
||||
reg:
|
||||
required: true
|
||||
|
||||
interrupts:
|
||||
required: true
|
||||
|
||||
clock_div8:
|
||||
description: Option enables clock divide by a factor of 8.
|
||||
type: boolean
|
||||
|
||||
input-src:
|
||||
description: Connects the CAN input line to a specific IO.
|
||||
type: string
|
||||
required: true
|
||||
enum:
|
||||
- "RXDA"
|
||||
- "RXDB"
|
||||
- "RXDC"
|
||||
- "RXDD"
|
||||
- "RXDE"
|
||||
- "RXDF"
|
||||
- "RXDG"
|
||||
- "RXDH"
|
24
dts/bindings/can/infineon,xmc4xxx-can.yaml
Normal file
24
dts/bindings/can/infineon,xmc4xxx-can.yaml
Normal file
|
@ -0,0 +1,24 @@
|
|||
# Copyright (c) 2023 Andriy Gelman
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
description: |
|
||||
Infineon XMC4xxx CAN
|
||||
|
||||
compatible: "infineon,xmc4xxx-can"
|
||||
|
||||
include: base.yaml
|
||||
|
||||
properties:
|
||||
reg:
|
||||
required: true
|
||||
|
||||
clock-prescaler:
|
||||
type: int
|
||||
required: true
|
||||
description: Clock divider for the input clock. Valid range is [1, 1023].
|
||||
|
||||
message-objects:
|
||||
type: int
|
||||
required: true
|
||||
description: Number of total can messages supported by hardware.
|
|
@ -60,4 +60,9 @@ config HAS_XMCLIB_ETH
|
|||
help
|
||||
Enable XMCLIB Ethernet MAC
|
||||
|
||||
config HAS_XMCLIB_CAN
|
||||
bool
|
||||
help
|
||||
Enable XMCLIB CAN
|
||||
|
||||
endif # HAS_XMCLIB
|
||||
|
|
|
@ -7,6 +7,7 @@ tests:
|
|||
integration_platforms:
|
||||
- native_sim
|
||||
filter: dt_chosen_enabled("zephyr,canbus") and not dt_compat_enabled("kvaser,pcican")
|
||||
and not dt_compat_enabled("infineon,xmc4xxx-can-node")
|
||||
harness: console
|
||||
harness_config:
|
||||
type: one_line
|
||||
|
|
|
@ -22,5 +22,6 @@ config SOC_SERIES_XMC_4XXX
|
|||
select HAS_XMCLIB_CCU
|
||||
select HAS_XMCLIB_WDT
|
||||
select HAS_XMCLIB_ETH
|
||||
select HAS_XMCLIB_CAN
|
||||
help
|
||||
Enable support for XMC 4xxx MCU series
|
||||
|
|
|
@ -6,8 +6,10 @@ common:
|
|||
tests:
|
||||
drivers.can.api:
|
||||
filter: dt_chosen_enabled("zephyr,canbus") and not dt_compat_enabled("kvaser,pcican")
|
||||
and not dt_compat_enabled("infineon,xmc4xxx-can-node")
|
||||
drivers.can.api.rtr:
|
||||
filter: dt_chosen_enabled("zephyr,canbus") and not dt_compat_enabled("kvaser,pcican")
|
||||
and not dt_compat_enabled("infineon,xmc4xxx-can-node")
|
||||
extra_configs:
|
||||
- CONFIG_CAN_ACCEPT_RTR=y
|
||||
drivers.can.api.twai:
|
||||
|
|
Loading…
Reference in a new issue