| /* |
| * Copyright (c) 2024 Renesas Electronics Corporation |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #include <soc.h> |
| #include <zephyr/logging/log.h> |
| #include <zephyr/drivers/clock_control/renesas_ra_cgc.h> |
| #include <zephyr/drivers/pinctrl.h> |
| #include <zephyr/drivers/can.h> |
| #include <zephyr/drivers/can/transceiver.h> |
| #include "r_can_api.h" |
| #include "r_canfd.h" |
| |
| LOG_MODULE_REGISTER(can_renesas_ra, CONFIG_CAN_LOG_LEVEL); |
| |
| #define DT_DRV_COMPAT renesas_ra_canfd |
| |
| #define CAN_RENESAS_RA_TIMING_MAX \ |
| { \ |
| .sjw = 128, \ |
| .prop_seg = 1, \ |
| .phase_seg1 = 255, \ |
| .phase_seg2 = 128, \ |
| .prescaler = 1024, \ |
| } |
| |
| #define CAN_RENESAS_RA_TIMING_MIN \ |
| { \ |
| .sjw = 1, \ |
| .prop_seg = 1, \ |
| .phase_seg1 = 2, \ |
| .phase_seg2 = 2, \ |
| .prescaler = 1, \ |
| } |
| |
| #ifdef CONFIG_CAN_FD_MODE |
| #define CAN_RENESAS_RA_TIMING_DATA_MAX \ |
| { \ |
| .sjw = 16, \ |
| .prop_seg = 1, \ |
| .phase_seg1 = 31, \ |
| .phase_seg2 = 16, \ |
| .prescaler = 128, \ |
| } |
| #define CAN_RENESAS_RA_TIMING_DATA_MIN \ |
| { \ |
| .sjw = 1, \ |
| .prop_seg = 1, \ |
| .phase_seg1 = 2, \ |
| .phase_seg2 = 2, \ |
| .prescaler = 1, \ |
| } |
| #endif |
| |
| /* This frame ID will be reserved. Any filter using this ID may cause undefined behavior. */ |
| #define CAN_RENESAS_RA_RESERVED_ID (CAN_EXT_ID_MASK) |
| |
| /* |
| * Common FIFO configuration: refer to '34.2.28 CFDCFCC : Common FIFO Configuration/Control |
| * Register' - RA8M1 MCU group HWM |
| */ |
| #define CANFD_CFG_COMMONFIFO0 (0U << R_CANFD_CFDCFCC_CFE_Pos) /* Common FIFO Disable */ |
| |
| #define CANFD_CFG_COMMONFIFO {CANFD_CFG_COMMONFIFO0} |
| |
| /* |
| * RX FIFO configuration: refer to '34.2.25 CFDRFCCa : RX FIFO Configuration/Control Registers' - |
| * RA8M1 MCU group HWM |
| */ |
| #define CANFD_CFG_RX_FIFO0 \ |
| ((1U << R_CANFD_CFDRFCC_RFE_Pos) | /* RX FIFO Enable */ \ |
| (1U << R_CANFD_CFDRFCC_RFIE_Pos) | /* RX FIFO Interrupt Enable */ \ |
| (7U << R_CANFD_CFDRFCC_RFPLS_Pos) | /* RX FIFO Payload Data Size: 64 */ \ |
| (3U << R_CANFD_CFDRFCC_RFDC_Pos) | /* RX FIFO Depth: 16 messages */ \ |
| (1U << R_CANFD_CFDRFCC_RFIM_Pos)) /* Interrupt generated at every received message \ |
| * storage \ |
| */ |
| |
| #define CANFD_CFG_RX_FIFO1 (0U << R_CANFD_CFDRFCC_RFE_Pos) /* RX FIFO Disable */ |
| |
| #define CANFD_CFG_RXFIFO {CANFD_CFG_RX_FIFO0, CANFD_CFG_RX_FIFO1} |
| |
| /* |
| * Global Configuration: refer to '34.2.11 CFDGCFG : Global Configuration Register' - RA8M1 MCU |
| * group HWM |
| */ |
| #define CANFD_CFG_GLOBAL \ |
| ((0U << R_CANFD_CFDGCFG_TPRI_Pos) | /* Transmission Priority: ID priority */ \ |
| (0U << R_CANFD_CFDGCFG_DCE_Pos) | /* DLC check disabled */ \ |
| (1U << R_CANFD_CFDGCFG_DCS_Pos)) /* DLL Clock Select: CANFDCLK */ |
| |
| /* |
| * TX Message Buffer Interrupt Enable Configuration: refer to '34.2.43 CFDTMIEC : TX Message Buffer |
| * Interrupt Enable Configuration Register' - RA8M1 MCU group HWM |
| */ |
| #define CANFD_CFG_TXMB_TXI_ENABLE (BIT(0)) /* Enable TXMB0 interrupt */ |
| |
| /* |
| * Number and size of RX Message Buffers: refer to '34.2.23 CFDRMNB : RX Message Buffer Number |
| * Register' - RA8M1 MCU group HWM |
| */ |
| #define CANFD_CFG_RXMB (0U << R_CANFD_CFDRMNB_NRXMB_Pos) /* Number of RX Message Buffers: 0 */ |
| |
| /* |
| * Channel Error IRQ configurations: refer to '34.2.3 CFDC0CTR : Control Register' - RA8M1 MCU group |
| * HWM |
| */ |
| #define CANFD_CFG_ERR_IRQ \ |
| (BIT(R_CANFD_CFDC_CTR_EWIE_Pos) | /* Error Warning Interrupt Enable */ \ |
| BIT(R_CANFD_CFDC_CTR_EPIE_Pos) | /* Error Passive Interrupt Enable */ \ |
| BIT(R_CANFD_CFDC_CTR_BOEIE_Pos) | /* Bus-Off Entry Interrupt Enable */ \ |
| BIT(R_CANFD_CFDC_CTR_BORIE_Pos) | /* Bus-Off Recovery Interrupt Enable */ \ |
| BIT(R_CANFD_CFDC_CTR_OLIE_Pos)) /* Overload Interrupt Enable */ |
| |
| /* |
| * Global Error IRQ configurations: refer to '34.2.12 CFDGCTR : Global Control Register' - RA8M1 MCU |
| * group HWM |
| */ |
| #define CANFD_CFG_GLERR_IRQ \ |
| ((3UL << R_CANFD_CFDGCTR_GMDC_Pos) | /* Global Mode Control: Keep current value */ \ |
| (0UL << R_CANFD_CFDGCTR_DEIE_Pos) | /* DLC check interrupt disabled */ \ |
| (0UL << R_CANFD_CFDGCTR_MEIE_Pos) | /* Message lost error interrupt disabled */ \ |
| (0UL << R_CANFD_CFDGCTR_THLEIE_Pos) | /* TX history list entry lost interrupt disabled */ \ |
| (0UL << R_CANFD_CFDGCTR_CMPOFIE_Pos)) /* CANFD message payload overflow flag interrupt \ |
| * disabled \ |
| */ |
| |
| /* Keycode to enable/disable accessing to AFL entry */ |
| #define CFDGAFLIGNCTR_KEY_CODE (0xC4UL) |
| |
| /* Default Dataphase bitrate configuration in case classic mode is enabled */ |
| static const can_bit_timing_cfg_t classic_can_data_timing_default = { |
| .baud_rate_prescaler = 1, |
| .time_segment_1 = 3, |
| .time_segment_2 = 2, |
| .synchronization_jump_width = 1, |
| }; |
| |
| struct can_renesas_ra_global_cfg { |
| const struct device *op_clk; |
| const struct device *ram_clk; |
| const struct clock_control_ra_subsys_cfg op_subsys; |
| const struct clock_control_ra_subsys_cfg ram_subsys; |
| }; |
| |
| struct can_renesas_ra_filter { |
| bool set; |
| struct can_filter filter; |
| can_rx_callback_t rx_cb; |
| void *rx_usr_data; |
| }; |
| |
| struct can_renesas_ra_cfg { |
| struct can_driver_config common; |
| const struct pinctrl_dev_config *pcfg; |
| const struct device *global_dev; |
| const struct device *dll_clk; |
| const struct clock_control_ra_subsys_cfg dll_subsys; |
| const uint32_t rx_filter_num; |
| }; |
| |
| struct can_renesas_ra_data { |
| struct can_driver_data common; |
| struct k_mutex inst_mutex; |
| const struct device *dev; |
| can_instance_t fsp_can; |
| can_tx_callback_t tx_cb; |
| struct k_sem tx_sem; |
| void *tx_usr_data; |
| struct can_renesas_ra_filter *rx_filter; |
| can_bit_timing_cfg_t data_timing; |
| }; |
| |
| extern void canfd_error_isr(void); |
| extern void canfd_rx_fifo_isr(void); |
| extern void canfd_common_fifo_rx_isr(void); |
| extern void canfd_channel_tx_isr(void); |
| |
| static const can_api_t *can_api = &g_canfd_on_canfd; |
| |
| static inline int set_hw_timing_configuration(const struct device *dev, |
| can_bit_timing_cfg_t *f_timing, |
| const struct can_timing *z_timing) |
| { |
| struct can_renesas_ra_data *data = dev->data; |
| |
| if (f_timing == NULL || z_timing == NULL) { |
| return -EINVAL; |
| } |
| |
| k_mutex_lock(&data->inst_mutex, K_FOREVER); |
| |
| *f_timing = (can_bit_timing_cfg_t){ |
| .baud_rate_prescaler = z_timing->prescaler, |
| .time_segment_1 = z_timing->prop_seg + z_timing->phase_seg1, |
| .time_segment_2 = z_timing->phase_seg2, |
| .synchronization_jump_width = z_timing->sjw, |
| }; |
| |
| k_mutex_unlock(&data->inst_mutex); |
| |
| return 0; |
| } |
| |
| static inline int get_free_filter_id(const struct device *dev) |
| { |
| struct can_renesas_ra_data *data = dev->data; |
| const struct can_renesas_ra_cfg *cfg = dev->config; |
| |
| for (uint32_t filter_id = 0; filter_id < cfg->rx_filter_num; filter_id++) { |
| if (!data->rx_filter[filter_id].set) { |
| return filter_id; |
| } |
| } |
| |
| return -ENOSPC; |
| } |
| |
| static void set_afl_rule(const struct device *dev, const struct can_filter *filter, |
| uint32_t afl_offset) |
| { |
| struct can_renesas_ra_data *data = dev->data; |
| canfd_extended_cfg_t const *p_extend = data->fsp_can.p_cfg->p_extend; |
| canfd_afl_entry_t *afl = (canfd_afl_entry_t *)&p_extend->p_afl[afl_offset]; |
| |
| *afl = (canfd_afl_entry_t){.id = {.id = filter->id, |
| #ifndef CONFIG_CAN_ACCEPT_RTR |
| .frame_type = CAN_FRAME_TYPE_DATA, |
| #endif |
| .id_mode = ((filter->flags & CAN_FILTER_IDE) != 0) |
| ? CAN_ID_MODE_EXTENDED |
| : CAN_ID_MODE_STANDARD}, |
| .mask = {.mask_id = filter->mask, |
| #ifdef CONFIG_CAN_ACCEPT_RTR |
| /* Accept all types of frames */ |
| .mask_frame_type = 0, |
| #else |
| /* Only accept frames with the configured frametype */ |
| .mask_frame_type = 1, |
| #endif |
| .mask_id_mode = ((filter->flags & CAN_FILTER_IDE) != 0) |
| ? CAN_ID_MODE_EXTENDED |
| : CAN_ID_MODE_STANDARD}, |
| .destination = {.fifo_select_flags = CANFD_RX_FIFO_0}}; |
| |
| if (data->common.started) { |
| /* These steps help update AFL rules while CAN module running */ |
| canfd_instance_ctrl_t *p_ctrl = (canfd_instance_ctrl_t *)data->fsp_can.p_ctrl; |
| R_CANFD_Type *reg = p_ctrl->p_reg; |
| |
| /* Ignore AFL entry which will be changed */ |
| reg->CFDGAFLIGNENT_b.IRN = afl_offset; |
| reg->CFDGAFLIGNCTR = ((CFDGAFLIGNCTR_KEY_CODE << R_CANFD_CFDGAFLIGNCTR_KEY_Pos) | |
| BIT(R_CANFD_CFDGAFLIGNCTR_IREN_Pos)); |
| reg->CFDGAFLECTR_b.AFLDAE = 1; |
| |
| /* Write AFL configuration */ |
| reg->CFDGAFL[afl_offset] = *(R_CANFD_CFDGAFL_Type *)afl; |
| |
| /* Lock AFL entry access */ |
| reg->CFDGAFLECTR_b.AFLDAE = 0; |
| reg->CFDGAFLIGNCTR = ((CFDGAFLIGNCTR_KEY_CODE << R_CANFD_CFDGAFLIGNCTR_KEY_Pos)); |
| } |
| } |
| |
| static void remove_afl_rule(const struct device *dev, uint32_t afl_offset) |
| { |
| struct can_renesas_ra_data *data = dev->data; |
| canfd_extended_cfg_t const *p_extend = data->fsp_can.p_cfg->p_extend; |
| canfd_afl_entry_t *afl = (canfd_afl_entry_t *)&p_extend->p_afl[afl_offset]; |
| |
| /* Set the AFL ID to reserved ID */ |
| *afl = (canfd_afl_entry_t){ |
| .id = {.id = CAN_RENESAS_RA_RESERVED_ID, .id_mode = CAN_ID_MODE_EXTENDED}, |
| .mask = {.mask_id = CAN_RENESAS_RA_RESERVED_ID, |
| .mask_id_mode = CAN_ID_MODE_EXTENDED}}; |
| |
| if (data->common.started) { |
| /* These steps help update AFL rules while CAN module running */ |
| canfd_instance_ctrl_t *p_ctrl = (canfd_instance_ctrl_t *)data->fsp_can.p_ctrl; |
| R_CANFD_Type *reg = p_ctrl->p_reg; |
| |
| /* Ignore AFL entry which will be changed */ |
| reg->CFDGAFLIGNENT_b.IRN = afl_offset; |
| reg->CFDGAFLIGNCTR = ((CFDGAFLIGNCTR_KEY_CODE << R_CANFD_CFDGAFLIGNCTR_KEY_Pos) | |
| BIT(R_CANFD_CFDGAFLIGNCTR_IREN_Pos)); |
| reg->CFDGAFLECTR_b.AFLDAE = 1; |
| |
| /* Change configurations for AFL entry */ |
| reg->CFDGAFL[afl_offset] = *(R_CANFD_CFDGAFL_Type *)(afl); |
| |
| /* Lock AFL entry access */ |
| reg->CFDGAFLECTR_b.AFLDAE = 0; |
| reg->CFDGAFLIGNCTR = ((CFDGAFLIGNCTR_KEY_CODE << R_CANFD_CFDGAFLIGNCTR_KEY_Pos)); |
| } |
| } |
| |
| #ifdef CONFIG_CAN_MANUAL_RECOVERY_MODE |
| static int recover_bus(const struct device *dev, k_timeout_t timeout) |
| { |
| struct can_renesas_ra_data *data = dev->data; |
| canfd_instance_ctrl_t *p_ctrl = data->fsp_can.p_ctrl; |
| R_CANFD_Type *reg = p_ctrl->p_reg; |
| uint32_t cfdcnctr = reg->CFDC->CTR; |
| int ret = 0; |
| |
| if (reg->CFDC->ERFL_b.BOEF == 0) { |
| /* Channel bus-off entry not detected */ |
| goto end; |
| } |
| |
| reg->CFDC->CTR_b.BOM = 0x00; /* Switch to Normal Bus-Off mode (comply with ISO 11898-1) */ |
| reg->CFDC->CTR_b.RTBO = 1; /* Force channel state to return from bus-off */ |
| |
| int64_t start_ticks = k_uptime_ticks(); |
| |
| while (reg->CFDC->ERFL_b.BORF == 0) { |
| if ((k_uptime_ticks() - start_ticks) > timeout.ticks) { |
| ret = -EAGAIN; |
| goto end; |
| } |
| } |
| end: |
| reg->CFDC->CTR = cfdcnctr; /* Restore channel configuration */ |
| return ret; |
| } |
| #endif |
| |
| static inline void can_renesas_ra_call_tx_cb(const struct device *dev, int err) |
| { |
| struct can_renesas_ra_data *data = dev->data; |
| can_tx_callback_t cb = data->tx_cb; |
| |
| if (cb != NULL) { |
| data->tx_cb = NULL; |
| cb(dev, err, data->tx_usr_data); |
| k_sem_give(&data->tx_sem); |
| } |
| } |
| |
| static inline void can_renesas_ra_call_rx_cb(const struct device *dev, can_callback_args_t *p_args) |
| { |
| struct can_renesas_ra_data *data = dev->data; |
| struct can_renesas_ra_filter *rx_filter = data->rx_filter; |
| const struct can_renesas_ra_cfg *cfg = dev->config; |
| struct can_frame frame = { |
| .dlc = can_bytes_to_dlc(p_args->frame.data_length_code), |
| .id = p_args->frame.id, |
| .flags = (((p_args->frame.id_mode == CAN_ID_MODE_EXTENDED) ? CAN_FRAME_IDE : 0UL) | |
| ((p_args->frame.type == CAN_FRAME_TYPE_REMOTE) ? CAN_FRAME_RTR : 0UL) | |
| ((p_args->frame.options & CANFD_FRAME_OPTION_FD) != 0 ? CAN_FRAME_FDF |
| : 0UL) | |
| ((p_args->frame.options & CANFD_FRAME_OPTION_ERROR) != 0 ? CAN_FRAME_ESI |
| : 0UL) | |
| ((p_args->frame.options & CANFD_FRAME_OPTION_BRS) != 0 ? CAN_FRAME_BRS |
| : 0UL)), |
| }; |
| |
| memcpy(frame.data, p_args->frame.data, p_args->frame.data_length_code); |
| |
| for (uint32_t i = 0; i < cfg->rx_filter_num; i++) { |
| can_rx_callback_t cb = rx_filter->rx_cb; |
| void *usr_data = rx_filter->rx_usr_data; |
| |
| if (cb == NULL) { |
| rx_filter++; |
| continue; /* this filter has not set yet */ |
| } |
| |
| if (!can_frame_matches_filter(&frame, &rx_filter->filter)) { |
| rx_filter++; |
| continue; /* filter did not match */ |
| } |
| |
| cb(dev, &frame, usr_data); |
| break; |
| } |
| } |
| |
| static inline void can_renesas_ra_call_state_change_cb(const struct device *dev, |
| enum can_state state) |
| { |
| struct can_renesas_ra_data *data = dev->data; |
| can_info_t can_info; |
| |
| if (FSP_SUCCESS != can_api->infoGet(data->fsp_can.p_ctrl, &can_info)) { |
| LOG_DBG("get state info failed"); |
| return; |
| } |
| |
| struct can_bus_err_cnt err_cnt = { |
| .rx_err_cnt = can_info.error_count_receive, |
| .tx_err_cnt = can_info.error_count_transmit, |
| }; |
| |
| data->common.state_change_cb(dev, state, err_cnt, data->common.state_change_cb_user_data); |
| } |
| |
| static int can_renesas_ra_get_capabilities(const struct device *dev, can_mode_t *cap) |
| { |
| ARG_UNUSED(dev); |
| *cap = CAN_MODE_NORMAL | CAN_MODE_LOOPBACK; |
| |
| #ifdef CONFIG_CAN_FD_MODE |
| *cap |= CAN_MODE_FD; |
| #endif |
| |
| #ifdef CONFIG_CAN_MANUAL_RECOVERY_MODE |
| *cap |= CAN_MODE_MANUAL_RECOVERY; |
| #endif |
| |
| return 0; |
| } |
| |
| static int can_renesas_ra_start(const struct device *dev) |
| { |
| struct can_renesas_ra_data *data = dev->data; |
| const struct device *transceiver_dev = can_get_transceiver(dev); |
| int ret = 0; |
| |
| if (!device_is_ready(dev)) { |
| return -EIO; |
| } |
| |
| if (data->common.started) { |
| return -EALREADY; |
| } |
| |
| if (((transceiver_dev != NULL) && |
| can_transceiver_enable(transceiver_dev, data->common.mode))) { |
| LOG_DBG("CAN transceiver enable failed"); |
| return -EIO; |
| } |
| |
| k_mutex_lock(&data->inst_mutex, K_FOREVER); |
| canfd_extended_cfg_t *p_extend = (canfd_extended_cfg_t *)data->fsp_can.p_cfg->p_extend; |
| |
| p_extend->p_data_timing = |
| (can_bit_timing_cfg_t *)((data->common.mode & CAN_MODE_FD) != 0 |
| ? &data->data_timing |
| : &classic_can_data_timing_default); |
| |
| if (FSP_SUCCESS != can_api->close(data->fsp_can.p_ctrl)) { |
| LOG_DBG("CAN close failed"); |
| ret = -EIO; |
| goto end; |
| } |
| |
| if (FSP_SUCCESS != can_api->open(data->fsp_can.p_ctrl, data->fsp_can.p_cfg)) { |
| LOG_DBG("CAN open failed"); |
| ret = -EIO; |
| goto end; |
| } |
| |
| if ((data->common.mode & CAN_MODE_LOOPBACK) != 0) { |
| if (FSP_SUCCESS != can_api->modeTransition(data->fsp_can.p_ctrl, |
| CAN_OPERATION_MODE_NORMAL, |
| CAN_TEST_MODE_LOOPBACK_INTERNAL)) { |
| LOG_DBG("CAN mode change failed"); |
| ret = -EIO; |
| goto end; |
| } |
| } |
| |
| data->common.started = true; |
| end: |
| k_mutex_unlock(&data->inst_mutex); |
| return ret; |
| } |
| |
| static int can_renesas_ra_stop(const struct device *dev) |
| { |
| struct can_renesas_ra_data *data = dev->data; |
| const struct device *transceiver_dev = can_get_transceiver(dev); |
| int ret = 0; |
| |
| if (!data->common.started) { |
| return -EALREADY; |
| } |
| |
| k_mutex_lock(&data->inst_mutex, K_FOREVER); |
| |
| if (FSP_SUCCESS != can_api->modeTransition(data->fsp_can.p_ctrl, CAN_OPERATION_MODE_HALT, |
| CAN_TEST_MODE_DISABLED)) { |
| LOG_DBG("CAN stop failed"); |
| ret = -EIO; |
| goto end; |
| } |
| |
| if (((transceiver_dev != NULL) && can_transceiver_disable(transceiver_dev))) { |
| LOG_DBG("CAN transceiver disable failed"); |
| ret = -EIO; |
| goto end; |
| } |
| |
| if (data->tx_cb != NULL) { |
| data->tx_cb = NULL; |
| k_sem_give(&data->tx_sem); |
| } |
| |
| data->common.started = false; |
| |
| end: |
| k_mutex_unlock(&data->inst_mutex); |
| return ret; |
| } |
| |
| static int can_renesas_ra_set_mode(const struct device *dev, can_mode_t mode) |
| { |
| struct can_renesas_ra_data *data = dev->data; |
| int ret = 0; |
| |
| if (data->common.started) { |
| /* CAN controller should be in stopped state */ |
| return -EBUSY; |
| } |
| |
| k_mutex_lock(&data->inst_mutex, K_FOREVER); |
| |
| can_mode_t caps = 0; |
| |
| ret = can_renesas_ra_get_capabilities(dev, &caps); |
| if (ret != 0) { |
| goto end; |
| } |
| |
| if ((mode & ~caps) != 0) { |
| ret = -ENOTSUP; |
| goto end; |
| } |
| |
| data->common.mode = mode; |
| |
| end: |
| k_mutex_unlock(&data->inst_mutex); |
| return ret; |
| } |
| |
| static int can_renesas_ra_set_timing(const struct device *dev, const struct can_timing *timing) |
| { |
| struct can_renesas_ra_data *data = dev->data; |
| |
| if (data->common.started) { |
| /* Device is not in stopped state */ |
| return -EBUSY; |
| } |
| |
| return set_hw_timing_configuration(dev, data->fsp_can.p_cfg->p_bit_timing, timing); |
| } |
| |
| static int can_renesas_ra_send(const struct device *dev, const struct can_frame *frame, |
| k_timeout_t timeout, can_tx_callback_t callback, void *user_data) |
| { |
| struct can_renesas_ra_data *data = dev->data; |
| |
| if (!data->common.started) { |
| return -ENETDOWN; |
| } |
| |
| #ifdef CONFIG_CAN_FD_MODE |
| if ((frame->flags & ~(CAN_FRAME_IDE | CAN_FRAME_RTR | CAN_FRAME_FDF | CAN_FRAME_BRS)) != |
| 0) { |
| LOG_ERR("unsupported CAN frame flags 0x%02x", frame->flags); |
| return -ENOTSUP; |
| } |
| |
| if ((data->common.mode & CAN_MODE_FD) == 0U && |
| ((frame->flags & (CAN_FRAME_FDF | CAN_FRAME_BRS)) != 0U)) { |
| LOG_ERR("CAN FD format not supported in non-FD mode"); |
| return -ENOTSUP; |
| } |
| #else /* CONFIG_CAN_FD_MODE */ |
| if ((frame->flags & ~(CAN_FRAME_IDE | CAN_FRAME_RTR)) != 0U) { |
| LOG_ERR("unsupported CAN frame flags 0x%02x", frame->flags); |
| return -ENOTSUP; |
| } |
| #endif /* !CONFIG_CAN_FD_MODE */ |
| |
| if ((frame->flags & CAN_FRAME_FDF) != 0U) { |
| if (frame->dlc > CANFD_MAX_DLC) { |
| LOG_ERR("DLC of %d for CAN FD format frame", frame->dlc); |
| return -EINVAL; |
| } |
| } else { |
| if (frame->dlc > CAN_MAX_DLC) { |
| LOG_ERR("DLC of %d for non-FD format frame", frame->dlc); |
| return -EINVAL; |
| } |
| } |
| |
| if (k_sem_take(&data->tx_sem, timeout) != 0) { |
| return -EAGAIN; |
| } |
| |
| k_mutex_lock(&data->inst_mutex, K_FOREVER); |
| int ret = 0; |
| |
| data->tx_cb = callback; |
| data->tx_usr_data = user_data; |
| |
| can_frame_t fsp_frame = { |
| .id = frame->id, |
| .id_mode = ((frame->flags & CAN_FRAME_IDE) != 0) ? CAN_ID_MODE_EXTENDED |
| : CAN_ID_MODE_STANDARD, |
| .type = ((frame->flags & CAN_FRAME_RTR) != 0) ? CAN_FRAME_TYPE_REMOTE |
| : CAN_FRAME_TYPE_DATA, |
| .data_length_code = can_dlc_to_bytes(frame->dlc), |
| .options = |
| ((((frame->flags & CAN_FRAME_FDF) != 0) ? CANFD_FRAME_OPTION_FD : 0UL) | |
| (((frame->flags & CAN_FRAME_BRS) != 0) ? CANFD_FRAME_OPTION_BRS : 0UL) | |
| (((frame->flags & CAN_FRAME_ESI) != 0) ? CANFD_FRAME_OPTION_ERROR : 0UL)), |
| }; |
| |
| memcpy(fsp_frame.data, frame->data, fsp_frame.data_length_code); |
| |
| if (FSP_SUCCESS != can_api->write(data->fsp_can.p_ctrl, CANFD_TX_BUFFER_0, &fsp_frame)) { |
| LOG_DBG("CAN transmit failed"); |
| data->tx_cb = NULL; |
| data->tx_usr_data = NULL; |
| k_sem_give(&data->tx_sem); |
| ret = -EIO; |
| goto end; |
| } |
| |
| end: |
| k_mutex_unlock(&data->inst_mutex); |
| return ret; |
| } |
| |
| static int can_renesas_ra_add_rx_filter(const struct device *dev, can_rx_callback_t callback, |
| void *user_data, const struct can_filter *filter) |
| { |
| struct can_renesas_ra_data *data = dev->data; |
| int filter_id = -ENOSPC; |
| |
| k_mutex_lock(&data->inst_mutex, K_FOREVER); |
| |
| filter_id = get_free_filter_id(dev); |
| if (filter_id == -ENOSPC) { |
| goto end; |
| } |
| |
| set_afl_rule(dev, filter, filter_id); |
| |
| memcpy(&data->rx_filter[filter_id].filter, filter, sizeof(struct can_filter)); |
| data->rx_filter[filter_id].rx_cb = callback; |
| data->rx_filter[filter_id].rx_usr_data = user_data; |
| data->rx_filter[filter_id].set = true; |
| |
| end: |
| k_mutex_unlock(&data->inst_mutex); |
| return filter_id; |
| } |
| |
| static void can_renesas_ra_remove_rx_filter(const struct device *dev, int filter_id) |
| { |
| struct can_renesas_ra_data *data = dev->data; |
| const struct can_renesas_ra_cfg *cfg = dev->config; |
| |
| if ((filter_id < 0) || (filter_id >= cfg->rx_filter_num)) { |
| return; |
| } |
| |
| k_mutex_lock(&data->inst_mutex, K_FOREVER); |
| |
| remove_afl_rule(dev, filter_id); |
| |
| data->rx_filter[filter_id].rx_cb = NULL; |
| data->rx_filter[filter_id].rx_usr_data = NULL; |
| data->rx_filter[filter_id].set = false; |
| |
| k_mutex_unlock(&data->inst_mutex); |
| } |
| |
| #ifdef CONFIG_CAN_MANUAL_RECOVERY_MODE |
| static int can_renesas_ra_recover(const struct device *dev, k_timeout_t timeout) |
| { |
| struct can_renesas_ra_data *data = dev->data; |
| |
| if (!data->common.started) { |
| return -ENETDOWN; |
| } |
| |
| if ((data->common.mode & CAN_MODE_MANUAL_RECOVERY) == 0) { |
| return -ENOTSUP; |
| } |
| |
| return recover_bus(dev, timeout); |
| } |
| #endif |
| |
| static int can_renesas_ra_get_state(const struct device *dev, enum can_state *state, |
| struct can_bus_err_cnt *err_cnt) |
| { |
| struct can_renesas_ra_data *data = dev->data; |
| can_info_t fsp_info = {0}; |
| |
| if (state == NULL && err_cnt == NULL) { |
| return 0; |
| } |
| |
| if (FSP_SUCCESS != can_api->infoGet(data->fsp_can.p_ctrl, &fsp_info)) { |
| LOG_DBG("CAN get state info failed"); |
| return -EIO; |
| } |
| |
| if (state != NULL) { |
| if (!data->common.started) { |
| *state = CAN_STATE_STOPPED; |
| } else { |
| if ((fsp_info.error_code & R_CANFD_CFDC_ERFL_BOEF_Msk) != 0) { |
| *state = CAN_STATE_BUS_OFF; |
| } else if ((fsp_info.error_code & R_CANFD_CFDC_ERFL_EPF_Msk) != 0) { |
| *state = CAN_STATE_ERROR_PASSIVE; |
| } else if ((fsp_info.error_code & R_CANFD_CFDC_ERFL_EWF_Msk) != 0) { |
| *state = CAN_STATE_ERROR_WARNING; |
| } else if ((fsp_info.error_code & R_CANFD_CFDC_ERFL_BEF_Msk) != 0) { |
| *state = CAN_STATE_ERROR_ACTIVE; |
| } |
| } |
| } |
| |
| if (err_cnt != NULL) { |
| err_cnt->tx_err_cnt = fsp_info.error_count_transmit; |
| err_cnt->rx_err_cnt = fsp_info.error_count_receive; |
| } |
| |
| return 0; |
| } |
| |
| static void can_renesas_ra_set_state_change_callback(const struct device *dev, |
| can_state_change_callback_t callback, |
| void *user_data) |
| { |
| struct can_renesas_ra_data *data = dev->data; |
| canfd_instance_ctrl_t *p_ctrl = data->fsp_can.p_ctrl; |
| int key = irq_lock(); |
| |
| k_mutex_lock(&data->inst_mutex, K_FOREVER); |
| |
| if (callback != NULL) { |
| /* Enable state change interrupt */ |
| p_ctrl->p_reg->CFDC->CTR |= (uint32_t)CANFD_CFG_ERR_IRQ; |
| } else { |
| /* Disable state change interrupt */ |
| p_ctrl->p_reg->CFDC->CTR &= (uint32_t)~CANFD_CFG_ERR_IRQ; |
| |
| /* Clear state change interrupt flags */ |
| p_ctrl->p_reg->CFDC->ERFL &= |
| ~(BIT(R_CANFD_CFDC_ERFL_BOEF_Pos) | BIT(R_CANFD_CFDC_ERFL_EWF_Pos) | |
| BIT(R_CANFD_CFDC_ERFL_EPF_Pos) | BIT(R_CANFD_CFDC_ERFL_BOEF_Pos)); |
| } |
| |
| data->common.state_change_cb = callback; |
| data->common.state_change_cb_user_data = user_data; |
| |
| k_mutex_unlock(&data->inst_mutex); |
| |
| irq_unlock(key); |
| } |
| |
| static int can_renesas_ra_get_core_clock(const struct device *dev, uint32_t *rate) |
| { |
| const struct can_renesas_ra_cfg *cfg = dev->config; |
| clock_control_subsys_t dll_subsys = (clock_control_subsys_t)&cfg->dll_subsys; |
| |
| return clock_control_get_rate(cfg->dll_clk, dll_subsys, rate); |
| } |
| |
| static int can_renesas_ra_get_max_filters(const struct device *dev, bool ide) |
| { |
| ARG_UNUSED(ide); |
| const struct can_renesas_ra_cfg *cfg = dev->config; |
| |
| return cfg->rx_filter_num; |
| } |
| |
| #ifdef CONFIG_CAN_FD_MODE |
| static int can_renesas_ra_set_timing_data(const struct device *dev, |
| const struct can_timing *timing_data) |
| { |
| struct can_renesas_ra_data *data = dev->data; |
| |
| if (data->common.started) { |
| return -EBUSY; |
| } |
| |
| return set_hw_timing_configuration(dev, &data->data_timing, timing_data); |
| } |
| #endif /* CONFIG_CAN_FD_MODE */ |
| |
| void can_renesas_ra_fsp_cb(can_callback_args_t *p_args) |
| { |
| const struct device *dev = p_args->p_context; |
| |
| switch (p_args->event) { |
| case CAN_EVENT_RX_COMPLETE: { |
| can_renesas_ra_call_rx_cb(dev, p_args); |
| break; |
| } |
| |
| case CAN_EVENT_TX_COMPLETE: { |
| can_renesas_ra_call_tx_cb(dev, 0); |
| break; |
| } |
| |
| case CAN_EVENT_ERR_CHANNEL: { |
| if ((p_args->error & R_CANFD_CFDC_ERFL_BEF_Msk) != 0) { |
| can_renesas_ra_call_state_change_cb(dev, CAN_STATE_ERROR_ACTIVE); |
| } |
| if ((p_args->error & R_CANFD_CFDC_ERFL_EWF_Msk) != 0) { |
| can_renesas_ra_call_state_change_cb(dev, CAN_STATE_ERROR_WARNING); |
| } |
| if ((p_args->error & R_CANFD_CFDC_ERFL_EPF_Msk) != 0) { |
| can_renesas_ra_call_state_change_cb(dev, CAN_STATE_ERROR_PASSIVE); |
| } |
| if ((p_args->error & R_CANFD_CFDC_ERFL_BOEF_Msk) != 0) { |
| can_renesas_ra_call_state_change_cb(dev, CAN_STATE_BUS_OFF); |
| } |
| |
| if ((p_args->error & R_CANFD_CFDC_ERFL_ALF_Msk) != 0) { /* Arbitration Lost Error */ |
| can_renesas_ra_call_tx_cb(dev, -EBUSY); |
| } |
| if ((p_args->error & (R_CANFD_CFDC_ERFL_AERR_Msk | /* ACK Error */ |
| R_CANFD_CFDC_ERFL_ADERR_Msk | /* ACK Delimiter Error */ |
| R_CANFD_CFDC_ERFL_B1ERR_Msk | /* Bit 1 Error */ |
| R_CANFD_CFDC_ERFL_B0ERR_Msk)) /* Bit 0 Error */ |
| != 0) { |
| can_renesas_ra_call_tx_cb(dev, -EIO); |
| } |
| } |
| |
| default: |
| break; |
| } |
| } |
| |
| static inline int can_renesas_ra_apply_default_config(const struct device *dev) |
| { |
| struct can_renesas_ra_cfg *cfg = (struct can_renesas_ra_cfg *)dev->config; |
| int ret; |
| |
| struct can_timing timing = {0}; |
| |
| /* Calculate bit_timing parameter */ |
| ret = can_calc_timing(dev, &timing, cfg->common.bitrate, cfg->common.sample_point); |
| if (ret < 0) { |
| return ret; |
| } |
| |
| ret = can_renesas_ra_set_timing(dev, &timing); |
| if (ret != 0) { |
| return ret; |
| } |
| |
| #ifdef CONFIG_CAN_FD_MODE |
| can_calc_timing_data(dev, &timing, cfg->common.bitrate_data, cfg->common.sample_point_data); |
| ret = can_renesas_ra_set_timing_data(dev, &timing); |
| if (ret < 0) { |
| return ret; |
| } |
| #endif |
| |
| for (uint32_t filter_id = 0; filter_id < cfg->rx_filter_num; filter_id++) { |
| remove_afl_rule(dev, filter_id); |
| } |
| |
| return 0; |
| } |
| |
| static inline int can_renesas_module_clock_init(const struct device *dev) |
| { |
| const struct can_renesas_ra_cfg *cfg = dev->config; |
| const struct can_renesas_ra_global_cfg *global_cfg = cfg->global_dev->config; |
| int ret; |
| |
| ret = clock_control_on(cfg->dll_clk, (clock_control_subsys_t)&cfg->dll_subsys); |
| if (ret < 0) { |
| return -EIO; |
| } |
| |
| uint32_t op_rate; |
| uint32_t ram_rate; |
| uint32_t dll_rate; |
| |
| ret = clock_control_get_rate(global_cfg->op_clk, |
| (clock_control_subsys_t)&global_cfg->op_subsys, &op_rate); |
| if (ret < 0) { |
| return ret; |
| } |
| |
| ret = clock_control_get_rate(global_cfg->ram_clk, |
| (clock_control_subsys_t)&global_cfg->ram_subsys, &ram_rate); |
| if (ret < 0) { |
| return ret; |
| } |
| |
| ret = clock_control_get_rate(cfg->dll_clk, (clock_control_subsys_t)&cfg->dll_subsys, |
| &dll_rate); |
| if (ret < 0) { |
| return ret; |
| } |
| |
| /* Clock constraint: refer to '34.1.2 Clock restriction' - RA8M1 MCU group HWM */ |
| /* |
| * Operation clock rate must be at least 40Mhz in case CANFD mode. |
| * Otherwise, it must be at least 32MHz. |
| */ |
| if (IS_ENABLED(CONFIG_CAN_FD_MODE) ? op_rate < 40000000 : op_rate < 32000000) { |
| return -ENOTSUP; |
| } |
| /* |
| * (RAM clock rate / 2) >= DLL rate |
| * (CANFD operation clock rate) >= DLL rate |
| */ |
| if ((ram_rate / 2) < dll_rate || op_rate < dll_rate) { |
| return -ENOTSUP; |
| } |
| |
| return 0; |
| } |
| |
| static int can_renesas_ra_init(const struct device *dev) |
| { |
| const struct can_renesas_ra_cfg *cfg = dev->config; |
| struct can_renesas_ra_data *data = dev->data; |
| int ret = 0; |
| |
| k_mutex_init(&data->inst_mutex); |
| k_sem_init(&data->tx_sem, 1, 1); |
| data->common.started = false; |
| |
| ret = can_renesas_module_clock_init(dev); |
| if (ret < 0) { |
| LOG_DBG("clock initialize failed"); |
| return ret; |
| } |
| |
| /* Configure dt provided device signals when available */ |
| ret = pinctrl_apply_state(cfg->pcfg, PINCTRL_STATE_DEFAULT); |
| if (ret < 0) { |
| LOG_DBG("pin function initial failed"); |
| return ret; |
| } |
| |
| /* Apply config and setting for CAN controller HW */ |
| ret = can_renesas_ra_apply_default_config(dev); |
| if (ret < 0) { |
| LOG_DBG("invalid default configuration"); |
| return ret; |
| } |
| |
| ret = can_api->open(data->fsp_can.p_ctrl, data->fsp_can.p_cfg); |
| if (ret != FSP_SUCCESS) { |
| LOG_DBG("CAN bus initialize failed"); |
| return -EIO; |
| } |
| |
| /* Put CAN controller into stopped state */ |
| ret = can_api->modeTransition(data->fsp_can.p_ctrl, CAN_OPERATION_MODE_HALT, |
| CAN_TEST_MODE_DISABLED); |
| if (ret != FSP_SUCCESS) { |
| can_api->close(data->fsp_can.p_ctrl); |
| LOG_DBG("CAN bus initialize failed"); |
| return -EIO; |
| } |
| |
| return 0; |
| } |
| |
| static const struct can_driver_api can_renesas_ra_driver_api = { |
| .get_capabilities = can_renesas_ra_get_capabilities, |
| .start = can_renesas_ra_start, |
| .stop = can_renesas_ra_stop, |
| .set_mode = can_renesas_ra_set_mode, |
| .set_timing = can_renesas_ra_set_timing, |
| .send = can_renesas_ra_send, |
| .add_rx_filter = can_renesas_ra_add_rx_filter, |
| .remove_rx_filter = can_renesas_ra_remove_rx_filter, |
| #if defined(CONFIG_CAN_MANUAL_RECOVERY_MODE) |
| .recover = can_renesas_ra_recover, |
| #endif /* CONFIG_CAN_MANUAL_RECOVERY_MODE */ |
| .get_state = can_renesas_ra_get_state, |
| .set_state_change_callback = can_renesas_ra_set_state_change_callback, |
| .get_core_clock = can_renesas_ra_get_core_clock, |
| .get_max_filters = can_renesas_ra_get_max_filters, |
| .timing_min = CAN_RENESAS_RA_TIMING_MIN, |
| .timing_max = CAN_RENESAS_RA_TIMING_MAX, |
| #if defined(CONFIG_CAN_FD_MODE) |
| .set_timing_data = can_renesas_ra_set_timing_data, |
| .timing_data_min = CAN_RENESAS_RA_TIMING_DATA_MIN, |
| .timing_data_max = CAN_RENESAS_RA_TIMING_DATA_MAX, |
| #endif /* CONFIG_CAN_FD_MODE */ |
| }; |
| |
| #define CAN_RENESAS_RA_GLOBAL_IRQ_INIT() \ |
| R_ICU->IELSR_b[VECTOR_NUMBER_CAN_GLERR].IELS = ELC_EVENT_CAN_GLERR; \ |
| R_ICU->IELSR_b[VECTOR_NUMBER_CAN_RXF].IELS = ELC_EVENT_CAN_RXF; \ |
| IRQ_CONNECT(VECTOR_NUMBER_CAN_GLERR, \ |
| DT_IRQ_BY_NAME(DT_COMPAT_GET_ANY_STATUS_OKAY(renesas_ra_canfd_global), glerr, \ |
| priority), \ |
| canfd_error_isr, NULL, 0); \ |
| IRQ_CONNECT(VECTOR_NUMBER_CAN_RXF, \ |
| DT_IRQ_BY_NAME(DT_COMPAT_GET_ANY_STATUS_OKAY(renesas_ra_canfd_global), rxf, \ |
| priority), \ |
| canfd_rx_fifo_isr, NULL, 0); \ |
| irq_enable(VECTOR_NUMBER_CAN_RXF); \ |
| irq_enable(VECTOR_NUMBER_CAN_GLERR); |
| |
| static canfd_global_cfg_t g_canfd_global_cfg = { |
| .global_interrupts = CANFD_CFG_GLERR_IRQ, |
| .global_config = CANFD_CFG_GLOBAL, |
| .rx_mb_config = CANFD_CFG_RXMB, |
| .global_err_ipl = DT_IRQ_BY_NAME(DT_COMPAT_GET_ANY_STATUS_OKAY(renesas_ra_canfd_global), |
| glerr, priority), |
| .rx_fifo_ipl = DT_IRQ_BY_NAME(DT_COMPAT_GET_ANY_STATUS_OKAY(renesas_ra_canfd_global), rxf, |
| priority), |
| .rx_fifo_config = CANFD_CFG_RXFIFO, |
| .common_fifo_config = CANFD_CFG_COMMONFIFO, |
| }; |
| |
| static const struct can_renesas_ra_global_cfg g_can_renesas_ra_global_cfg = { |
| .op_clk = DEVICE_DT_GET(DT_CLOCKS_CTLR_BY_NAME( |
| DT_COMPAT_GET_ANY_STATUS_OKAY(renesas_ra_canfd_global), opclk)), |
| .ram_clk = DEVICE_DT_GET(DT_CLOCKS_CTLR_BY_NAME( |
| DT_COMPAT_GET_ANY_STATUS_OKAY(renesas_ra_canfd_global), ramclk)), |
| .op_subsys = {.mstp = DT_CLOCKS_CELL_BY_NAME( |
| DT_COMPAT_GET_ANY_STATUS_OKAY(renesas_ra_canfd_global), opclk, mstp), |
| .stop_bit = DT_CLOCKS_CELL_BY_NAME( |
| DT_COMPAT_GET_ANY_STATUS_OKAY(renesas_ra_canfd_global), opclk, |
| stop_bit)}, |
| .ram_subsys = {.mstp = DT_CLOCKS_CELL_BY_NAME( |
| DT_COMPAT_GET_ANY_STATUS_OKAY(renesas_ra_canfd_global), ramclk, |
| mstp), |
| .stop_bit = DT_CLOCKS_CELL_BY_NAME( |
| DT_COMPAT_GET_ANY_STATUS_OKAY(renesas_ra_canfd_global), ramclk, |
| stop_bit)}, |
| }; |
| |
| static int can_renesas_ra_global_init(const struct device *dev) |
| { |
| int ret; |
| const struct can_renesas_ra_global_cfg *cfg = dev->config; |
| |
| ret = clock_control_on(cfg->op_clk, (clock_control_subsys_t)&cfg->op_subsys); |
| if (ret < 0) { |
| LOG_DBG("clock initialize failed"); |
| return ret; |
| } |
| |
| ret = clock_control_on(cfg->ram_clk, (clock_control_subsys_t)&cfg->ram_subsys); |
| if (ret < 0) { |
| LOG_DBG("clock initialize failed"); |
| return ret; |
| } |
| |
| CAN_RENESAS_RA_GLOBAL_IRQ_INIT(); |
| |
| return 0; |
| } |
| |
| DEVICE_DT_DEFINE(DT_COMPAT_GET_ANY_STATUS_OKAY(renesas_ra_canfd_global), can_renesas_ra_global_init, |
| NULL, NULL, &g_can_renesas_ra_global_cfg, PRE_KERNEL_2, CONFIG_CAN_INIT_PRIORITY, |
| NULL) |
| |
| #define _ELC_EVENT_CAN_COMFRX(channel) ELC_EVENT_CAN##channel##_COMFRX |
| #define _ELC_EVENT_CAN_TX(channel) ELC_EVENT_CAN##channel##_TX |
| #define _ELC_EVENT_CAN_CHERR(channel) ELC_EVENT_CAN##channel##_CHERR |
| |
| #define ELC_EVENT_CAN_COMFRX(channel) _ELC_EVENT_CAN_COMFRX(channel) |
| #define ELC_EVENT_CAN_TX(channel) _ELC_EVENT_CAN_TX(channel) |
| #define ELC_EVENT_CAN_CHERR(channel) _ELC_EVENT_CAN_CHERR(channel) |
| |
| #define CAN_RENESAS_RA_CHANNEL_IRQ_INIT(index) \ |
| { \ |
| R_ICU->IELSR_b[DT_INST_IRQ_BY_NAME(index, rx, irq)].IELS = \ |
| ELC_EVENT_CAN_COMFRX(DT_INST_PROP(index, channel)); \ |
| R_ICU->IELSR_b[DT_INST_IRQ_BY_NAME(index, tx, irq)].IELS = \ |
| ELC_EVENT_CAN_TX(DT_INST_PROP(index, channel)); \ |
| R_ICU->IELSR_b[DT_INST_IRQ_BY_NAME(index, err, irq)].IELS = \ |
| ELC_EVENT_CAN_CHERR(DT_INST_PROP(index, channel)); \ |
| \ |
| IRQ_CONNECT(DT_INST_IRQ_BY_NAME(index, rx, irq), \ |
| DT_INST_IRQ_BY_NAME(index, rx, priority), canfd_common_fifo_rx_isr, \ |
| NULL, 0); \ |
| IRQ_CONNECT(DT_INST_IRQ_BY_NAME(index, tx, irq), \ |
| DT_INST_IRQ_BY_NAME(index, tx, priority), canfd_channel_tx_isr, NULL, \ |
| 0); \ |
| IRQ_CONNECT(DT_INST_IRQ_BY_NAME(index, err, irq), \ |
| DT_INST_IRQ_BY_NAME(index, err, priority), canfd_error_isr, NULL, 0); \ |
| \ |
| irq_enable(DT_INST_IRQ_BY_NAME(index, rx, irq)); \ |
| irq_enable(DT_INST_IRQ_BY_NAME(index, tx, irq)); \ |
| irq_enable(DT_INST_IRQ_BY_NAME(index, err, irq)); \ |
| } |
| |
| #define CAN_RENESAS_RA_INIT(index) \ |
| PINCTRL_DT_INST_DEFINE(index); \ |
| static canfd_afl_entry_t canfd_afl##index[DT_INST_PROP(index, rx_max_filters)]; \ |
| struct can_renesas_ra_filter \ |
| can_renesas_ra_rx_filter##index[DT_INST_PROP(index, rx_max_filters)]; \ |
| static can_bit_timing_cfg_t g_canfd_bit_timing##index; \ |
| static const struct can_renesas_ra_cfg can_renesas_ra_cfg##index = { \ |
| .common = CAN_DT_DRIVER_CONFIG_INST_GET(index, 0, 5000000), \ |
| .global_dev = DEVICE_DT_GET(DT_INST_PARENT(index)), \ |
| .pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(index), \ |
| .dll_clk = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR_BY_NAME(index, dllclk)), \ |
| .dll_subsys = \ |
| { \ |
| .mstp = DT_INST_CLOCKS_CELL_BY_NAME(index, dllclk, mstp), \ |
| .stop_bit = DT_INST_CLOCKS_CELL_BY_NAME(index, dllclk, stop_bit), \ |
| }, \ |
| .rx_filter_num = DT_INST_PROP(index, rx_max_filters), \ |
| }; \ |
| static canfd_instance_ctrl_t fsp_canfd_ctrl##index; \ |
| static canfd_extended_cfg_t fsp_canfd_extend####index = { \ |
| .p_afl = canfd_afl##index, \ |
| .txmb_txi_enable = CANFD_CFG_TXMB_TXI_ENABLE, \ |
| .error_interrupts = 0U, \ |
| .p_global_cfg = &g_canfd_global_cfg, \ |
| }; \ |
| static can_cfg_t fsp_canfd_cfg####index = { \ |
| .channel = DT_INST_PROP(index, channel), \ |
| .ipl = DT_INST_IRQ_BY_NAME(index, err, priority), \ |
| .error_irq = DT_INST_IRQ_BY_NAME(index, err, irq), \ |
| .rx_irq = DT_INST_IRQ_BY_NAME(index, rx, irq), \ |
| .tx_irq = DT_INST_IRQ_BY_NAME(index, tx, irq), \ |
| .p_extend = &fsp_canfd_extend##index, \ |
| .p_bit_timing = &g_canfd_bit_timing##index, \ |
| .p_context = DEVICE_DT_INST_GET(index), \ |
| .p_callback = can_renesas_ra_fsp_cb, \ |
| }; \ |
| static struct can_renesas_ra_data can_renesas_ra_data##index = { \ |
| .fsp_can = \ |
| { \ |
| .p_ctrl = &fsp_canfd_ctrl##index, \ |
| .p_cfg = &fsp_canfd_cfg##index, \ |
| .p_api = &g_canfd_on_canfd, \ |
| }, \ |
| .rx_filter = can_renesas_ra_rx_filter##index, \ |
| .dev = DEVICE_DT_INST_GET(index), \ |
| }; \ |
| static int can_renesas_ra_init##index(const struct device *dev) \ |
| { \ |
| const struct device *global_canfd = DEVICE_DT_GET(DT_INST_PARENT(index)); \ |
| if (!device_is_ready(global_canfd)) { \ |
| return -EIO; \ |
| } \ |
| CAN_RENESAS_RA_CHANNEL_IRQ_INIT(index) \ |
| return can_renesas_ra_init(dev); \ |
| } \ |
| CAN_DEVICE_DT_INST_DEFINE(index, can_renesas_ra_init##index, NULL, \ |
| &can_renesas_ra_data##index, &can_renesas_ra_cfg##index, \ |
| POST_KERNEL, CONFIG_CAN_INIT_PRIORITY, \ |
| &can_renesas_ra_driver_api); |
| |
| DT_INST_FOREACH_STATUS_OKAY(CAN_RENESAS_RA_INIT) |