| /* |
| * 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) |