Primary Git Repository for the Zephyr Project. Zephyr is a new generation, scalable, optimized, secure RTOS for multiple hardware architectures.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

745 lines
20 KiB

/*
* Copyright (c) 2024-2025 Analog Devices, Inc.
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT adi_max32_can
#include <zephyr/device.h>
#include <zephyr/drivers/can.h>
#include <zephyr/drivers/can/transceiver.h>
#include <zephyr/drivers/pinctrl.h>
#include <zephyr/drivers/clock_control/adi_max32_clock_control.h>
#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>
#include <can.h>
#include <wrap_max32_can.h>
LOG_MODULE_REGISTER(can_max32, CONFIG_CAN_LOG_LEVEL);
#define MAX32_CAN_MAX_DLC 8
#define MAX32_TX_CNT 1
#define MAX32_CAN_DRIVER_RXTX_BUF_SIZE 64
/* dev_list will be used only in object_event_callback() unit_event_callback() */
static const struct device *dev_list[MXC_CAN_INSTANCES];
struct max32_req_data {
mxc_can_req_t req;
mxc_can_msg_info_t info;
uint8_t buf[MAX32_CAN_DRIVER_RXTX_BUF_SIZE];
};
struct max32_can_tx_callback {
can_tx_callback_t function;
void *user_data;
};
struct max32_can_rx_callback {
can_rx_callback_t function;
void *user_data;
struct can_filter filter;
};
struct max32_can_data {
struct can_driver_data common;
enum can_state state;
struct k_mutex inst_mutex;
struct k_sem tx_sem;
struct max32_can_tx_callback tx_callback;
struct max32_req_data tx_data;
uint32_t filter_usage;
struct max32_can_rx_callback rx_callbacks[CONFIG_CAN_MAX_FILTER];
struct max32_req_data rx_data;
};
struct max32_can_config {
struct can_driver_config common;
mxc_can_regs_t *can;
uint8_t can_id;
uint8_t irqn;
void (*irq_config_func)(const struct device *dev);
const struct device *clock;
struct max32_perclk perclk;
const struct pinctrl_dev_config *pcfg;
};
static void can_max32_convert_canframe_to_req(const struct can_frame *msg, mxc_can_req_t *req)
{
mxc_can_msg_info_t *info = req->msg_info;
if (msg->flags & CAN_FRAME_IDE) {
info->msg_id = MXC_CAN_EXTENDED_ID(msg->id);
} else {
info->msg_id = MXC_CAN_STANDARD_ID(msg->id);
}
info->rtr = ((msg->flags & CAN_FRAME_RTR) != 0) ? 1 : 0;
info->dlc = msg->dlc;
req->data_sz = MIN(CAN_MAX_DLEN, can_dlc_to_bytes(msg->dlc));
memcpy(req->data, msg->data, req->data_sz * sizeof(uint8_t));
}
static void can_max32_convert_req_to_canframe(const mxc_can_req_t *req, struct can_frame *msg)
{
mxc_can_msg_info_t *info = req->msg_info;
memset(msg, 0, sizeof(struct can_frame));
if (info->msg_id & MXC_CAN_MSG_INFO_IDE_BIT) {
msg->id = (info->msg_id & CAN_EXT_ID_MASK);
msg->flags |= CAN_FRAME_IDE;
} else {
msg->id = (info->msg_id & CAN_STD_ID_MASK);
}
if (info->rtr) {
msg->flags |= CAN_FRAME_RTR;
}
msg->dlc = info->dlc;
int dlc_bytes = MIN(CAN_MAX_DLEN, can_dlc_to_bytes(info->dlc));
memcpy(msg->data, req->data, dlc_bytes * sizeof(uint8_t));
}
static int can_max32_get_capabilities(const struct device *dev, can_mode_t *cap)
{
ARG_UNUSED(dev);
*cap = CAN_MODE_NORMAL | CAN_MODE_LOOPBACK | CAN_MODE_LISTENONLY;
return 0;
}
static int can_max32_set_mode(const struct device *dev, can_mode_t mode)
{
struct max32_can_data *dev_data = dev->data;
can_mode_t cap;
(void)can_get_capabilities(dev, &cap);
if (dev_data->common.started) {
return -EBUSY;
}
if ((mode & ~(cap)) != 0) {
LOG_ERR("unsupported mode: 0x%08x; Capabilities: 0x%08x", mode, cap);
return -ENOTSUP;
}
dev_data->common.mode = mode;
return 0;
}
static int can_max32_get_core_clock(const struct device *dev, uint32_t *rate)
{
const struct max32_can_config *dev_cfg = dev->config;
*rate = MXC_CAN_GetClock(dev_cfg->can_id);
return 0;
}
static int can_max32_init_timing_struct(struct can_timing *timing, const struct device *dev)
{
const struct max32_can_config *dev_cfg = dev->config;
int ret = 0;
ret = can_calc_timing(dev, timing, dev_cfg->common.bitrate,
dev_cfg->common.sample_point);
if (ret < 0) {
LOG_ERR("can_calc_timing error sample_point: %d!",
dev_cfg->common.sample_point);
}
LOG_DBG("Bitrate: %d", dev_cfg->common.bitrate);
LOG_DBG("Presc: %d, PG1: %d, PG2: %d", timing->prescaler,
timing->prop_seg + timing->phase_seg1, timing->phase_seg2);
return ret;
}
static int can_max32_set_timing(const struct device *dev, const struct can_timing *timing)
{
const struct max32_can_config *dev_cfg = dev->config;
struct max32_can_data *dev_data = dev->data;
mxc_can_regs_t *can = dev_cfg->can;
uint32_t nbt_reg = 0;
if (!timing) {
LOG_ERR("timing structure is null");
return -EINVAL;
}
if (dev_data->common.started) {
return -EBUSY;
}
k_mutex_lock(&dev_data->inst_mutex, K_FOREVER);
can->mode |= MXC_F_CAN_MODE_RST;
nbt_reg |= FIELD_PREP(MXC_F_CAN_NBT_NBRP, timing->prescaler - 1);
nbt_reg |= FIELD_PREP(MXC_F_CAN_NBT_NSEG1, timing->prop_seg + timing->phase_seg1 - 1);
nbt_reg |= FIELD_PREP(MXC_F_CAN_NBT_NSEG2, timing->phase_seg2 - 1);
nbt_reg |= FIELD_PREP(MXC_F_CAN_NBT_NSJW, timing->sjw - 1);
can->nbt = nbt_reg;
can->mode &= ~MXC_F_CAN_MODE_RST;
k_mutex_unlock(&dev_data->inst_mutex);
return 0;
}
static int can_max32_start(const struct device *dev)
{
const struct max32_can_config *dev_cfg = dev->config;
struct max32_can_data *dev_data = dev->data;
can_mode_t mode;
int ret = 0;
k_mutex_lock(&dev_data->inst_mutex, K_FOREVER);
if (dev_data->common.started) {
ret = -EALREADY;
goto unlock;
}
mode = dev_data->common.mode;
if (dev_cfg->common.phy != NULL) {
ret = can_transceiver_enable(dev_cfg->common.phy, mode);
if (ret != 0) {
LOG_ERR("failed to enable CAN transceiver (err %d)", ret);
goto unlock;
}
}
CAN_STATS_RESET(dev);
if (((mode & CAN_MODE_LOOPBACK) != 0) && ((mode & CAN_MODE_LISTENONLY) != 0)) {
ret = MXC_CAN_SetMode(dev_cfg->can_id, MXC_CAN_MODE_LOOPBACK_W_TXD);
} else if ((mode & CAN_MODE_LOOPBACK) != 0) {
ret = MXC_CAN_SetMode(dev_cfg->can_id, MXC_CAN_MODE_LOOPBACK);
} else if ((mode & CAN_MODE_LISTENONLY) != 0) {
ret = MXC_CAN_SetMode(dev_cfg->can_id, MXC_CAN_MODE_MONITOR);
} else {
ret = MXC_CAN_SetMode(dev_cfg->can_id, MXC_CAN_MODE_NORMAL);
}
dev_data->common.started = true;
unlock:
k_mutex_unlock(&dev_data->inst_mutex);
return ret;
}
static int can_max32_stop(const struct device *dev)
{
const struct max32_can_config *dev_cfg = dev->config;
struct max32_can_data *dev_data = dev->data;
int ret = 0;
k_mutex_lock(&dev_data->inst_mutex, K_FOREVER);
if (!dev_data->common.started) {
ret = -EALREADY;
goto unlock;
}
if (dev_cfg->common.phy != NULL) {
ret = can_transceiver_disable(dev_cfg->common.phy);
if (ret != 0) {
LOG_ERR("failed to enable CAN transceiver (err %d)", ret);
goto unlock;
}
}
ret = MXC_CAN_SetMode(dev_cfg->can_id, MXC_CAN_MODE_INITIALIZATION);
if (ret < 0) {
LOG_ERR("failed to stop CAN controller (err %d)", ret);
goto unlock;
}
dev_data->state = CAN_STATE_STOPPED;
dev_data->common.started = false;
unlock:
k_mutex_unlock(&dev_data->inst_mutex);
return ret;
}
static int can_max32_send(const struct device *dev, const struct can_frame *msg,
k_timeout_t timeout, can_tx_callback_t callback, void *user_data)
{
struct max32_can_data *dev_data = dev->data;
int ret = 0;
unsigned int key;
LOG_DBG("Sending %d bytes. Id: 0x%x, ID type: %s %s", can_dlc_to_bytes(msg->dlc),
msg->id, (msg->flags & CAN_FRAME_IDE) ? "extended" : "standard",
(msg->flags & CAN_FRAME_RTR) ? "RTR" : "");
if (msg->dlc > MAX32_CAN_MAX_DLC) {
LOG_ERR("DLC of %d exceeds maximum (%d)", msg->dlc, MAX32_CAN_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->inst_mutex, K_FOREVER);
key = irq_lock();
dev_data->tx_callback.function = callback;
dev_data->tx_callback.user_data = user_data;
irq_unlock(key);
can_max32_convert_canframe_to_req(msg, &dev_data->tx_data.req);
ret = MXC_CAN_MessageSendAsync(0, &dev_data->tx_data.req);
if (ret < 0) {
LOG_ERR("MXC_CAN_MessageSendAsync error (err %d)", ret);
k_sem_give(&dev_data->tx_sem);
}
k_mutex_unlock(&dev_data->inst_mutex);
return 0;
}
static int can_max32_add_rx_filter(const struct device *dev, can_rx_callback_t callback,
void *user_data, const struct can_filter *filter)
{
struct max32_can_data *dev_data = dev->data;
int filter_idx = 0;
__ASSERT(callback != NULL, "rx_filter callback can not be null");
if ((filter->flags & ~CAN_FILTER_IDE) != 0) {
LOG_ERR("Unsupported CAN filter flags 0x%02x", filter->flags);
return -ENOTSUP;
}
k_mutex_lock(&dev_data->inst_mutex, K_FOREVER);
/* find free filter */
while ((BIT(filter_idx) & dev_data->filter_usage) && (filter_idx < CONFIG_CAN_MAX_FILTER)) {
filter_idx++;
}
/* setup filter */
if (filter_idx < CONFIG_CAN_MAX_FILTER) {
unsigned int key = irq_lock();
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;
dev_data->rx_callbacks[filter_idx].filter = *filter;
irq_unlock(key);
LOG_DBG("Set filter id:%08X mask:%08X", filter->id, filter->mask);
} else {
filter_idx = -ENOSPC;
LOG_WRN("All filters are used CONFIG_CAN_MAX_FILTER=%d", CONFIG_CAN_MAX_FILTER);
}
k_mutex_unlock(&dev_data->inst_mutex);
return filter_idx;
}
static void can_max32_remove_rx_filter(const struct device *dev, int filter_idx)
{
struct max32_can_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->inst_mutex, K_FOREVER);
if ((dev_data->filter_usage & BIT(filter_idx)) == 0) {
k_mutex_unlock(&dev_data->inst_mutex);
LOG_WRN("Filter is already not used filter_id=%d", filter_idx);
return;
}
key = irq_lock();
dev_data->filter_usage &= ~BIT(filter_idx);
dev_data->rx_callbacks[filter_idx].function = NULL;
dev_data->rx_callbacks[filter_idx].user_data = NULL;
dev_data->rx_callbacks[filter_idx].filter = (struct can_filter){0};
irq_unlock(key);
k_mutex_unlock(&dev_data->inst_mutex);
}
static void can_max32_state_change_handler(const struct device *dev, enum can_state old_state)
{
struct max32_can_data *dev_data = dev->data;
struct can_bus_err_cnt err_cnt;
enum can_state new_state;
can_state_change_callback_t state_change_cb;
state_change_cb = dev_data->common.state_change_cb;
can_get_state(dev, &new_state, &err_cnt);
if (old_state != new_state) {
if (state_change_cb) {
state_change_cb(dev, new_state, err_cnt,
dev_data->common.state_change_cb_user_data);
}
}
}
static int can_max32_get_state(const struct device *dev, enum can_state *state,
struct can_bus_err_cnt *err_cnt)
{
const struct max32_can_config *dev_cfg = dev->config;
struct max32_can_data *dev_data = dev->data;
mxc_can_regs_t *can = dev_cfg->can;
if (err_cnt != NULL) {
err_cnt->tx_err_cnt = can->txerr;
err_cnt->rx_err_cnt = can->rxerr;
}
if (state != NULL) {
*state = dev_data->state;
}
return 0;
}
static void can_max32_set_state_change_callback(const struct device *dev,
can_state_change_callback_t cb, void *user_data)
{
struct max32_can_data *dev_data = dev->data;
unsigned int key;
key = irq_lock();
dev_data->common.state_change_cb = cb;
dev_data->common.state_change_cb_user_data = user_data;
irq_unlock(key);
}
static int can_max32_get_max_filters(const struct device *dev, bool ide)
{
ARG_UNUSED(dev);
ARG_UNUSED(ide);
return CONFIG_CAN_MAX_FILTER;
}
#ifdef CONFIG_CAN_MANUAL_RECOVERY_MODE
static int can_max32_recover(const struct device *dev, k_timeout_t timeout)
{
const struct max32_can_data *dev_data = dev->data;
ARG_UNUSED(timeout);
if (!dev_data->common.started) {
return -ENETDOWN;
}
/* The MAX32 CAN controller does not support manual recovery. */
return -ENOSYS;
}
#endif
static void can_max32_init_req(struct max32_req_data *data)
{
mxc_can_req_t *req = &data->req;
req->msg_info = &data->info;
req->data = data->buf;
req->data_sz = sizeof(data->buf);
}
/* MAX32 CAN hardware has a very limited hardware filtering feature.
* This function implements a software filter to match the received CAN frames
* against the registered filters.
*/
static void can_max32_rx_soft_filter(const struct device *dev, struct can_frame *frame)
{
struct max32_can_data *dev_data = dev->data;
uint8_t filter_id = 0U;
can_rx_callback_t callback;
struct can_frame tmp_frame;
#ifndef CONFIG_CAN_ACCEPT_RTR
if ((frame->flags & CAN_FRAME_RTR) != 0U) {
return;
}
#endif /* !CONFIG_CAN_ACCEPT_RTR */
for (; filter_id < CONFIG_CAN_MAX_FILTER; filter_id++) {
if (!(BIT(filter_id) & dev_data->filter_usage)) {
continue; /* filter slot empty */
}
struct max32_can_rx_callback *rx_cb = &dev_data->rx_callbacks[filter_id];
if (!can_frame_matches_filter(frame, &rx_cb->filter)) {
continue; /* filter did not match */
}
callback = rx_cb->function;
/*Make a temporary copy in case the user modifies the message*/
tmp_frame = *frame;
callback(dev, &tmp_frame, rx_cb->user_data);
}
}
void can_max32_rx_handler(const struct device *dev)
{
struct max32_can_data *dev_data = dev->data;
struct can_frame msg;
can_max32_convert_req_to_canframe(&dev_data->rx_data.req, &msg);
can_max32_rx_soft_filter(dev, &msg);
}
void can_max32_tx_handler(const struct device *dev, int status)
{
struct max32_can_data *dev_data = dev->data;
can_tx_callback_t callback = dev_data->tx_callback.function;
if (callback != NULL) {
callback(dev, status, dev_data->tx_callback.user_data);
dev_data->tx_callback.function = NULL;
dev_data->tx_callback.user_data = NULL;
}
k_sem_give(&dev_data->tx_sem); /* to allow next tx request */
}
static void can_max32_isr(const struct device *dev)
{
const struct max32_can_config *dev_cfg = dev->config;
MXC_CAN_Handler(dev_cfg->can_id);
}
void object_event_callback(uint32_t can_idx, uint32_t event)
{
const struct device *dev = dev_list[can_idx];
if (event == MXC_CAN_OBJ_EVT_TX_COMPLETE) { /* Message send complete */
can_max32_tx_handler(dev, 0);
}
if (event == MXC_CAN_OBJ_EVT_RX) { /* Message receive complete */
can_max32_rx_handler(dev);
}
}
void unit_event_callback(uint32_t can_idx, uint32_t event)
{
const struct device *dev = dev_list[can_idx];
struct max32_can_data *dev_data = dev->data;
enum can_state old_state = dev_data->state;
switch (event) {
case MXC_CAN_UNIT_EVT_INACTIVE:
dev_data->state = CAN_STATE_STOPPED;
break;
case MXC_CAN_UNIT_EVT_ACTIVE:
dev_data->state = CAN_STATE_ERROR_ACTIVE;
break;
case MXC_CAN_UNIT_EVT_WARNING:
dev_data->state = CAN_STATE_ERROR_WARNING;
break;
case MXC_CAN_UNIT_EVT_PASSIVE:
dev_data->state = CAN_STATE_ERROR_PASSIVE;
break;
case MXC_CAN_UNIT_EVT_BUS_OFF:
dev_data->state = CAN_STATE_BUS_OFF;
break;
default:
}
can_max32_state_change_handler(dev, old_state);
}
static int can_max32_init(const struct device *dev)
{
const struct max32_can_config *dev_cfg = dev->config;
struct max32_can_data *dev_data = dev->data;
struct can_timing timing = {0};
int ret = 0;
k_mutex_init(&dev_data->inst_mutex);
k_sem_init(&dev_data->tx_sem, 1, 1);
if (dev_cfg->common.phy != NULL) {
if (!device_is_ready(dev_cfg->common.phy)) {
LOG_ERR("CAN transceiver not ready");
return -ENODEV;
}
}
if (!device_is_ready(dev_cfg->clock)) {
LOG_ERR("CAN clock is not ready");
return -ENODEV;
}
ret = clock_control_on(dev_cfg->clock, (clock_control_subsys_t)&dev_cfg->perclk);
if (ret) {
LOG_ERR("CAN clock is not on");
return -EIO;
}
ret = pinctrl_apply_state(dev_cfg->pcfg, PINCTRL_STATE_DEFAULT);
if (ret < 0) {
LOG_ERR("CAN pinctrl apply error:%d", ret);
return ret;
}
dev_data->state = CAN_STATE_STOPPED;
dev_cfg->irq_config_func(dev);
dev_list[dev_cfg->can_id] = dev;
ret = Wrap_MXC_CAN_Init(dev_cfg->can_id, MXC_CAN_OBJ_CFG_TXRX, unit_event_callback,
object_event_callback);
if (ret < 0) {
LOG_ERR("Wrap_MXC_CAN_Init() failed:%d", ret);
return ret;
}
ret = can_max32_init_timing_struct(&timing, dev);
if (ret < 0) {
LOG_ERR("can_max32_init_timing_struct failed:%d", ret);
return ret;
}
ret = can_set_timing(dev, &timing);
if (ret < 0) {
LOG_ERR("can_set_timing failed:%d", ret);
return ret;
}
/* Since two hw filter exists all EXT and STD frames are allowed */
MXC_CAN_ObjectSetFilter(dev_cfg->can_id,
MXC_CAN_FILT_CFG_MASK_ADD | MXC_CAN_FILT_CFG_DUAL1_STD_ID,
CAN_STD_ID_MASK, 0);
MXC_CAN_ObjectSetFilter(dev_cfg->can_id,
MXC_CAN_FILT_CFG_MASK_ADD | MXC_CAN_FILT_CFG_DUAL2_EXT_ID,
CAN_EXT_ID_MASK, 0);
/* Initialize the async rx and tx request structures */
can_max32_init_req(&dev_data->tx_data);
can_max32_init_req(&dev_data->rx_data);
ret = can_set_mode(dev, CAN_MODE_NORMAL);
if (ret) {
return ret;
}
/* No need to call in each time a frame is received */
ret = MXC_CAN_MessageReadAsync(dev_cfg->can_id, &dev_data->rx_data.req);
return ret;
}
static DEVICE_API(can, can_max32_api) = {
.get_capabilities = can_max32_get_capabilities,
.set_mode = can_max32_set_mode,
.set_timing = can_max32_set_timing,
.start = can_max32_start,
.stop = can_max32_stop,
.send = can_max32_send,
.add_rx_filter = can_max32_add_rx_filter,
.remove_rx_filter = can_max32_remove_rx_filter,
#ifdef CONFIG_CAN_MANUAL_RECOVERY_MODE
.recover = can_max32_recover,
#endif
.get_state = can_max32_get_state,
.set_state_change_callback = can_max32_set_state_change_callback,
.get_core_clock = can_max32_get_core_clock,
.get_max_filters = can_max32_get_max_filters,
.timing_min = {
.sjw = 1,
.prop_seg = 0,
.phase_seg1 = 1,
.phase_seg2 = 1,
.prescaler = 1,
},
.timing_max = {
.sjw = 4,
.prop_seg = 0,
.phase_seg1 = 16,
.phase_seg2 = 8,
.prescaler = 64,
},
};
#define CAN_MAX32_IRQ_CONFIG_FUNC(n) \
static void can_max32_irq_config_func_##n(const struct device *dev) \
{ \
IRQ_CONNECT(DT_INST_IRQN(n), DT_INST_IRQ(n, priority), can_max32_isr, \
DEVICE_DT_INST_GET(n), 0); \
irq_enable(DT_INST_IRQN(n)); \
}
#define DEFINE_CAN_MAX32(inst) \
PINCTRL_DT_INST_DEFINE(inst); \
CAN_MAX32_IRQ_CONFIG_FUNC(inst) \
\
static struct max32_can_data max32_can_data_##inst; \
static const struct max32_can_config max32_can_config_##inst = { \
.common = CAN_DT_DRIVER_CONFIG_INST_GET(inst, 0, 1000000), \
.can = (mxc_can_regs_t *)DT_INST_REG_ADDR(inst), \
.can_id = MXC_CAN_GET_IDX((mxc_can_regs_t *)DT_INST_REG_ADDR(inst)), \
.irq_config_func = can_max32_irq_config_func_##inst, \
.clock = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(inst)), \
.perclk.bus = DT_INST_CLOCKS_CELL(inst, offset), \
.perclk.bit = DT_INST_CLOCKS_CELL(inst, bit), \
.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(inst), \
}; \
\
CAN_DEVICE_DT_INST_DEFINE(inst, can_max32_init, NULL, &max32_can_data_##inst, \
&max32_can_config_##inst, POST_KERNEL, CONFIG_CAN_INIT_PRIORITY, \
&can_max32_api);
DT_INST_FOREACH_STATUS_OKAY(DEFINE_CAN_MAX32)