| /* |
| * Copyright 2022-2024 NXP |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #include <zephyr/kernel.h> |
| #include <zephyr/sys/atomic.h> |
| #include <zephyr/drivers/can.h> |
| #include <zephyr/drivers/can/transceiver.h> |
| #include <zephyr/drivers/clock_control.h> |
| #include <zephyr/device.h> |
| #include <zephyr/drivers/pinctrl.h> |
| #include <zephyr/logging/log.h> |
| #include <zephyr/irq.h> |
| |
| #include <CanEXCEL_Ip_HwAccess.h> |
| #include <CanEXCEL_Ip_Irq.h> |
| |
| #define DT_DRV_COMPAT nxp_s32_canxl |
| |
| /* |
| * Convert from RX message buffer index to allocated filter ID and |
| * vice versa. |
| */ |
| #ifdef CONFIG_CAN_NXP_S32_RX_FIFO |
| #define RX_MBIDX_TO_ALLOC_IDX(x) (x) |
| #define ALLOC_IDX_TO_RXMB_IDX(x) (x) |
| #else |
| #define RX_MBIDX_TO_ALLOC_IDX(x) (x - CONFIG_CAN_NXP_S32_MAX_TX) |
| #define ALLOC_IDX_TO_RXMB_IDX(x) (x + CONFIG_CAN_NXP_S32_MAX_TX) |
| #endif |
| |
| |
| /* |
| * Convert from TX message buffer index to allocated TX ID and vice |
| * versa. |
| */ |
| #define TX_MBIDX_TO_ALLOC_IDX(x) (x) |
| #define ALLOC_IDX_TO_TXMB_IDX(x) (x) |
| |
| #define CAN_NXP_S32_TIMEOUT_MS 1 |
| #define CAN_NXP_S32_MAX_BITRATE 8000000 |
| #define CAN_NXP_S32_DATA_LENGTH 64 |
| |
| #define CAN_NXP_S32_TDCO_MAX FIELD_GET(CANXL_SIC_BTDCC_FTDCOFF_MASK, CANXL_SIC_BTDCC_FTDCOFF_MASK) |
| |
| #ifdef CONFIG_CAN_NXP_S32_RX_FIFO |
| /* RX FIFO depth is fixed to the maximum value */ |
| #define CAN_NXP_S32_RX_FIFO_DEPTH 32 |
| /* RX FIFO water mark equal 1 that allows the interrupt is generated after 1 message received */ |
| #define CAN_NXP_S32_RX_FIFO_WATERMARK 1 |
| #endif |
| |
| #if defined(CONFIG_CAN_FD_MODE) && defined(CONFIG_CAN_NXP_S32_RX_FIFO) |
| #define CAN_NXP_S32_FD_MODE 1 |
| #endif |
| |
| #define BCANXL_ST_STFERR_MASK BIT(4) |
| #define BCANXL_ST_FRMERR_MASK BIT(5) |
| #define BCANXL_ST_CRCERR_MASK BIT(6) |
| #define BCANXL_ST_ACKERR_MASK BIT(7) |
| #define BCANXL_ST_BIT0ERR_MASK BIT(8) |
| #define BCANXL_ST_BIT1ERR_MASK BIT(9) |
| #define BCANXL_ST_OVRERR_MASK BIT(15) |
| #define BCANXL_ST_FLTCONF(x) (uint8_t)((x << BCANXL_ST_FLTCONF_SHIFT) & BCANXL_ST_FLTCONF_MASK) |
| |
| #define CANXL_CLEAR_ALL_ERRORS 0x7FFF0F00U |
| |
| LOG_MODULE_REGISTER(nxp_s32_canxl, CONFIG_CAN_LOG_LEVEL); |
| |
| struct can_nxp_s32_config { |
| const struct can_driver_config common; |
| CANXL_SIC_Type *base_sic; |
| #ifdef CONFIG_CAN_NXP_S32_RX_FIFO |
| CANXL_RXFIFO_Type * base_rx_fifo; |
| CANXL_RXFIFO_CONTROL_Type *base_rx_fifo_ctrl; |
| #endif |
| CANXL_MRU_Type * base_mru; |
| uint8 instance; |
| const struct device *clock_dev; |
| clock_control_subsys_t clock_subsys; |
| const struct pinctrl_dev_config *pin_cfg; |
| Canexcel_Ip_ConfigType *can_cfg; |
| void (*irq_config_func)(void); |
| }; |
| |
| struct can_nxp_s32_tx_callback { |
| Canexcel_Ip_DataInfoType tx_info; |
| can_tx_callback_t function; |
| void *arg; |
| }; |
| |
| struct can_nxp_s32_rx_callback { |
| struct can_filter filter; |
| #ifndef CONFIG_CAN_NXP_S32_RX_FIFO |
| Canexcel_Ip_DataInfoType rx_info; |
| #endif |
| can_rx_callback_t function; |
| void *arg; |
| }; |
| |
| struct can_nxp_s32_data { |
| struct can_driver_data common; |
| Canexcel_Ip_StateType *can_state; |
| |
| ATOMIC_DEFINE(rx_allocs, CONFIG_CAN_NXP_S32_MAX_RX); |
| struct k_mutex rx_mutex; |
| struct can_nxp_s32_rx_callback rx_cbs[CONFIG_CAN_NXP_S32_MAX_RX]; |
| #ifndef CONFIG_CAN_NXP_S32_RX_FIFO |
| Canexcel_RxFdMsg *rx_msg; |
| #endif |
| |
| ATOMIC_DEFINE(tx_allocs, CONFIG_CAN_NXP_S32_MAX_TX); |
| struct k_sem tx_allocs_sem; |
| struct k_mutex tx_mutex; |
| struct can_nxp_s32_tx_callback tx_cbs[CONFIG_CAN_NXP_S32_MAX_TX]; |
| Canexcel_TxFdMsgType *tx_msg; |
| |
| #ifdef CONFIG_CAN_NXP_S32_RX_FIFO |
| Canexcel_Ip_RxFifoFilterID_ADDR * rx_fifo_filter; |
| Canexcel_RxFdMsg *rx_fifo; |
| #endif |
| |
| struct can_timing timing; |
| #ifdef CAN_NXP_S32_FD_MODE |
| struct can_timing timing_data; |
| #endif |
| enum can_state state; |
| }; |
| |
| static int can_nxp_s32_get_capabilities(const struct device *dev, can_mode_t *cap) |
| { |
| ARG_UNUSED(dev); |
| |
| *cap = CAN_MODE_NORMAL | CAN_MODE_LOOPBACK | CAN_MODE_LISTENONLY; |
| |
| if (IS_ENABLED(CONFIG_CAN_MANUAL_RECOVERY_MODE)) { |
| *cap |= CAN_MODE_MANUAL_RECOVERY; |
| } |
| |
| if (IS_ENABLED(CAN_NXP_S32_FD_MODE)) { |
| *cap |= CAN_MODE_FD; |
| } |
| |
| return 0; |
| } |
| |
| #ifdef CONFIG_CAN_NXP_S32_RX_FIFO |
| static void can_nxp_s32_config_rx_fifo_filter(const struct device *dev, int filter_id) |
| { |
| const struct can_nxp_s32_config *config = dev->config; |
| struct can_nxp_s32_data *data = dev->data; |
| |
| /* Lock the RxFIFO by System by reading register */ |
| (void)config->base_rx_fifo_ctrl->RXFSYSLOCK; |
| |
| CanXL_ConfigIDFilter(config->base_rx_fifo, |
| &data->rx_fifo_filter[filter_id], filter_id); |
| |
| if ((config->base_rx_fifo_ctrl->RXFCSTA & CANXL_RXFIFO_CONTROL_RXFCSTA_SYSLOCK_MASK) |
| == CANXL_RXFIFO_CONTROL_RXFCSTA_SYSLOCK_MASK) { |
| /* Clear the sys lock to enable transfers */ |
| config->base_rx_fifo_ctrl->RXFSYSLOCK = |
| CANXL_RXFIFO_CONTROL_RXFSYSLOCK_SYSLOCK_MASK; |
| } |
| } |
| |
| /* Get the RxFiFO filter matched with the received RxFIFO message queue */ |
| static inline int can_nxp_s32_get_rx_fifo_filter(struct can_nxp_s32_data *data, uint8_t queue_idx) |
| { |
| int alloc = -ENOSPC; |
| uint32_t mask; |
| |
| for (int filter_id = 0; filter_id < CONFIG_CAN_NXP_S32_MAX_RX; filter_id++) { |
| mask = data->rx_fifo_filter[filter_id].idAddrFilterL; |
| |
| if (mask == 0) { |
| continue; |
| } |
| |
| if ((data->rx_fifo[queue_idx].Header.Id & mask) == |
| (data->rx_fifo_filter[filter_id].idAddrFilterH & mask)) { |
| alloc = filter_id; |
| break; |
| } |
| } |
| |
| return alloc; |
| } |
| #endif |
| |
| static int can_nxp_s32_start(const struct device *dev) |
| { |
| const struct can_nxp_s32_config *config = dev->config; |
| struct can_nxp_s32_data *data = dev->data; |
| int err; |
| |
| if (data->common.started) { |
| return -EALREADY; |
| } |
| |
| if (config->common.phy != NULL) { |
| err = can_transceiver_enable(config->common.phy, data->common.mode); |
| if (err != 0) { |
| LOG_ERR("failed to enable CAN transceiver (err %d)", err); |
| return err; |
| } |
| } |
| |
| /* Reset statistics and clear all errors */ |
| CAN_STATS_RESET(dev); |
| Canexcel_Ip_ClearErrorStatus(config->instance, CANXL_CLEAR_ALL_ERRORS); |
| |
| data->common.started = true; |
| |
| return 0; |
| } |
| |
| static int can_nxp_s32_stop(const struct device *dev) |
| { |
| const struct can_nxp_s32_config *config = dev->config; |
| struct can_nxp_s32_data *data = dev->data; |
| can_tx_callback_t function; |
| void *arg; |
| int alloc; |
| int err; |
| |
| if (!data->common.started) { |
| return -EALREADY; |
| } |
| |
| data->common.started = false; |
| |
| /* Abort any pending TX frames before entering freeze mode */ |
| for (alloc = 0; alloc < CONFIG_CAN_NXP_S32_MAX_TX; alloc++) { |
| function = data->tx_cbs[alloc].function; |
| arg = data->tx_cbs[alloc].arg; |
| |
| if (atomic_test_and_clear_bit(data->tx_allocs, alloc)) { |
| if (Canexcel_Ip_DeactivateMD(config->instance, |
| ALLOC_IDX_TO_TXMB_IDX(alloc))) { |
| LOG_ERR("Can't abort message !"); |
| }; |
| |
| function(dev, -ENETDOWN, arg); |
| k_sem_give(&data->tx_allocs_sem); |
| } |
| } |
| |
| if (config->common.phy != NULL) { |
| err = can_transceiver_disable(config->common.phy); |
| if (err != 0) { |
| LOG_ERR("failed to disable CAN transceiver (err %d)", err); |
| return err; |
| } |
| } |
| |
| return 0; |
| } |
| |
| static void can_nxp_s32_set_fd_mode(CANXL_SIC_Type *base, |
| boolean enable_fd, |
| boolean enable_brs) |
| { |
| base->BCFG2 = (base->BCFG2 & ~CANXL_SIC_BCFG2_FDEN_MASK) | |
| CANXL_SIC_BCFG2_FDEN(enable_fd ? 1UL : 0UL); |
| base->BCFG1 = (base->BCFG1 & ~CANXL_SIC_BCFG1_FDRSDIS_MASK) | |
| CANXL_SIC_BCFG1_FDRSDIS(enable_brs ? 0UL : 1UL); |
| base->BTDCC &= ~(CANXL_SIC_BTDCC_FTDCEN_MASK | |
| CANXL_SIC_BTDCC_FTDCOFF_MASK); |
| } |
| |
| static int can_nxp_s32_set_mode(const struct device *dev, can_mode_t mode) |
| { |
| can_mode_t supported = CAN_MODE_LOOPBACK | CAN_MODE_LISTENONLY; |
| const struct can_nxp_s32_config *config = dev->config; |
| struct can_nxp_s32_data *data = dev->data; |
| Canexcel_Ip_ModesType can_nxp_s32_mode = CAN_MODE_NORMAL; |
| bool canfd = false; |
| bool brs = false; |
| |
| if (data->common.started) { |
| return -EBUSY; |
| } |
| |
| if (IS_ENABLED(CONFIG_CAN_MANUAL_RECOVERY_MODE)) { |
| supported |= CAN_MODE_MANUAL_RECOVERY; |
| } |
| |
| if (IS_ENABLED(CAN_NXP_S32_FD_MODE)) { |
| supported |= CAN_MODE_FD; |
| } |
| |
| if ((mode & ~(supported)) != 0) { |
| LOG_ERR("unsupported mode: 0x%08x", mode); |
| return -ENOTSUP; |
| } |
| |
| if ((mode & (CAN_MODE_LOOPBACK | CAN_MODE_LISTENONLY)) |
| == (CAN_MODE_LOOPBACK | CAN_MODE_LISTENONLY)) { |
| LOG_ERR("unsupported mode loopback and " |
| "mode listen-only at the same time: 0x%08x", mode); |
| return -ENOTSUP; |
| } |
| |
| canfd = !!(mode & CAN_MODE_FD); |
| brs = canfd; |
| |
| if (mode & CAN_MODE_LISTENONLY) { |
| can_nxp_s32_mode = CANEXCEL_LISTEN_ONLY_MODE; |
| } else if (mode & CAN_MODE_LOOPBACK) { |
| can_nxp_s32_mode = CANEXCEL_LOOPBACK_MODE; |
| } |
| |
| Canexcel_Ip_EnterFreezeMode(config->instance); |
| |
| can_nxp_s32_set_fd_mode(config->base_sic, canfd, brs); |
| |
| if (IS_ENABLED(CONFIG_CAN_MANUAL_RECOVERY_MODE)) { |
| Canexcel_Ip_StatusType status; |
| uint32_t options = 0U; |
| |
| if ((mode & CAN_MODE_MANUAL_RECOVERY) == 0U) { |
| options = CANXL_IP_BUSOFF_RECOVERY_U32; |
| } |
| |
| status = CanXL_ConfigCtrlOptions(config->base_sic, options); |
| if (status != CANEXCEL_STATUS_SUCCESS) { |
| return -EIO; |
| } |
| } |
| |
| CanXL_SetOperationMode(config->base_sic, can_nxp_s32_mode); |
| |
| Canexcel_Ip_ExitFreezeMode(config->instance); |
| |
| data->common.mode = mode; |
| |
| return 0; |
| } |
| |
| static int can_nxp_s32_get_core_clock(const struct device *dev, uint32_t *rate) |
| { |
| const struct can_nxp_s32_config *config = dev->config; |
| |
| __ASSERT_NO_MSG(rate != NULL); |
| |
| return clock_control_get_rate(config->clock_dev, config->clock_subsys, rate); |
| } |
| |
| static int can_nxp_s32_get_max_filters(const struct device *dev, bool ide) |
| { |
| ARG_UNUSED(ide); |
| |
| return CONFIG_CAN_NXP_S32_MAX_RX; |
| } |
| |
| static int can_nxp_s32_get_state(const struct device *dev, enum can_state *state, |
| struct can_bus_err_cnt *err_cnt) |
| { |
| const struct can_nxp_s32_config *config = dev->config; |
| struct can_nxp_s32_data *data = dev->data; |
| uint8_t bcanxl_status; |
| |
| if (state) { |
| if (!data->common.started) { |
| *state = CAN_STATE_STOPPED; |
| } else { |
| Canexcel_Ip_GetControllerStatus(config->instance, &bcanxl_status); |
| if (bcanxl_status & BCANXL_ST_FLTCONF(2)) { |
| *state = CAN_STATE_BUS_OFF; |
| } else if (bcanxl_status & BCANXL_ST_FLTCONF(1)) { |
| *state = CAN_STATE_ERROR_PASSIVE; |
| } else if (config->base_sic->SYSS & |
| (CANXL_SIC_SYSS_CRXWRN_MASK | CANXL_SIC_SYSS_CTXWRN_MASK)) { |
| *state = CAN_STATE_ERROR_WARNING; |
| } else { |
| *state = CAN_STATE_ERROR_ACTIVE; |
| } |
| } |
| } |
| |
| if (err_cnt) { |
| Canexcel_Ip_GetControllerTxErrorCounter(config->instance, &err_cnt->tx_err_cnt); |
| Canexcel_Ip_GetControllerRxErrorCounter(config->instance, &err_cnt->rx_err_cnt); |
| } |
| |
| return 0; |
| } |
| |
| static void can_nxp_s32_set_state_change_callback(const struct device *dev, |
| can_state_change_callback_t callback, |
| void *user_data) |
| { |
| struct can_nxp_s32_data *data = dev->data; |
| |
| data->common.state_change_cb = callback; |
| data->common.state_change_cb_user_data = user_data; |
| } |
| |
| #ifdef CONFIG_CAN_MANUAL_RECOVERY_MODE |
| static int can_nxp_s32_recover(const struct device *dev, k_timeout_t timeout) |
| { |
| const struct can_nxp_s32_config *config = dev->config; |
| struct can_nxp_s32_data *data = dev->data; |
| enum can_state state; |
| uint64_t start_time; |
| int ret = 0; |
| |
| if (!data->common.started) { |
| return -ENETDOWN; |
| } |
| |
| if ((data->common.mode & CAN_MODE_MANUAL_RECOVERY) == 0U) { |
| return -ENOTSUP; |
| } |
| |
| can_nxp_s32_get_state(dev, &state, NULL); |
| if (state != CAN_STATE_BUS_OFF) { |
| return 0; |
| } |
| |
| start_time = k_uptime_ticks(); |
| config->base_sic->BCFG1 &= (~CANXL_SIC_BCFG1_ABRDIS_MASK); |
| |
| if (!K_TIMEOUT_EQ(timeout, K_NO_WAIT)) { |
| can_nxp_s32_get_state(dev, &state, NULL); |
| |
| while (state == CAN_STATE_BUS_OFF) { |
| if (!K_TIMEOUT_EQ(timeout, K_FOREVER) && |
| k_uptime_ticks() - start_time >= timeout.ticks) { |
| ret = -EAGAIN; |
| } |
| |
| can_nxp_s32_get_state(dev, &state, NULL); |
| } |
| } |
| |
| config->base_sic->BCFG1 |= CANXL_SIC_BCFG1_ABRDIS_MASK; |
| |
| return ret; |
| } |
| #endif /* CONFIG_CAN_MANUAL_RECOVERY_MODE */ |
| |
| static void can_nxp_s32_remove_rx_filter(const struct device *dev, int filter_id) |
| { |
| const struct can_nxp_s32_config *config = dev->config; |
| struct can_nxp_s32_data *data = dev->data; |
| int mb_indx = ALLOC_IDX_TO_RXMB_IDX(filter_id); |
| |
| if (filter_id < 0 || filter_id >= CONFIG_CAN_NXP_S32_MAX_RX) { |
| LOG_ERR("filter ID %d out of bounds", filter_id); |
| return; |
| } |
| |
| k_mutex_lock(&data->rx_mutex, K_FOREVER); |
| |
| if (atomic_test_and_clear_bit(data->rx_allocs, filter_id)) { |
| #ifdef CONFIG_CAN_NXP_S32_RX_FIFO |
| data->rx_fifo_filter[mb_indx].idAddrFilterL = 0; |
| data->rx_fifo_filter[mb_indx].idAddrFilterH = 0; |
| |
| Canexcel_Ip_EnterFreezeMode(config->instance); |
| |
| can_nxp_s32_config_rx_fifo_filter(dev, mb_indx); |
| |
| Canexcel_Ip_ExitFreezeMode(config->instance); |
| #else |
| if (Canexcel_Ip_DeactivateMD(config->instance, mb_indx)) { |
| LOG_ERR("Can't abort message !"); |
| }; |
| #endif |
| |
| data->rx_cbs[filter_id].function = NULL; |
| data->rx_cbs[filter_id].arg = NULL; |
| data->rx_cbs[filter_id].filter = (struct can_filter){0}; |
| } else { |
| LOG_WRN("Filter ID %d already detached", filter_id); |
| } |
| |
| k_mutex_unlock(&data->rx_mutex); |
| } |
| |
| static int can_nxp_s32_add_rx_filter(const struct device *dev, |
| can_rx_callback_t callback, |
| void *user_data, |
| const struct can_filter *filter) |
| { |
| const struct can_nxp_s32_config *config = dev->config; |
| struct can_nxp_s32_data *data = dev->data; |
| int alloc = -ENOSPC; |
| int mb_indx; |
| uint32_t mask; |
| |
| if ((filter->flags & ~(CAN_FILTER_IDE)) != 0) { |
| LOG_ERR("unsupported CAN filter flags 0x%02x", filter->flags); |
| return -ENOTSUP; |
| } |
| |
| k_mutex_lock(&data->rx_mutex, K_FOREVER); |
| |
| /* Find and allocate RX message buffer */ |
| for (int i = 0; i < CONFIG_CAN_NXP_S32_MAX_RX; i++) { |
| if (!atomic_test_and_set_bit(data->rx_allocs, i)) { |
| alloc = i; |
| break; |
| } |
| } |
| |
| if (alloc == -ENOSPC) { |
| LOG_ERR("No free filter bank found"); |
| goto unlock; |
| } |
| |
| data->rx_cbs[alloc].function = callback; |
| data->rx_cbs[alloc].arg = user_data; |
| data->rx_cbs[alloc].filter = *filter; |
| |
| /* Set Rx Mb individual mask for */ |
| mb_indx = ALLOC_IDX_TO_RXMB_IDX(alloc); |
| if (!!(filter->flags & CAN_FILTER_IDE)) { |
| mask = filter->mask & CANXL_IP_ID_EXT_MASK; |
| } else { |
| mask = (filter->mask << CANXL_IP_ID_STD_SHIFT) & CANXL_IP_ID_STD_MASK; |
| } |
| |
| #ifndef CONFIG_CAN_ACCEPT_RTR |
| mask |= CANXL_MSG_DESCRIPTORS_MDFLT1FD_RTRMSK_MASK; |
| #endif /* !CONFIG_CAN_ACCEPT_RTR */ |
| |
| Canexcel_Ip_EnterFreezeMode(config->instance); |
| |
| #ifdef CONFIG_CAN_NXP_S32_RX_FIFO |
| uint32_t filter_id; |
| |
| if (!!(filter->flags & CAN_FILTER_IDE)) { |
| filter_id = filter->id & CANXL_IP_ID_EXT_MASK; |
| } else { |
| filter_id = (filter->id << CANXL_IP_ID_STD_SHIFT) & CANXL_IP_ID_STD_MASK; |
| } |
| |
| data->rx_fifo_filter[mb_indx].filterType = CANEXCEL_IP_RX_FIFO_MASK_FILTER; |
| data->rx_fifo_filter[mb_indx].idAddrFilterL = mask; |
| data->rx_fifo_filter[mb_indx].idAddrFilterH = filter_id; |
| |
| can_nxp_s32_config_rx_fifo_filter(dev, mb_indx); |
| #else |
| data->rx_cbs[alloc].rx_info = (Canexcel_Ip_DataInfoType) { |
| .frame = CANEXCEL_CLASIC_FRAME, |
| .idType = !!(filter->flags & CAN_FILTER_IDE) ? |
| CANEXCEL_MSG_ID_EXT : CANEXCEL_MSG_ID_STD, |
| .dataLength = CAN_NXP_S32_DATA_LENGTH, |
| }; |
| |
| Canexcel_Ip_SetRxIndividualMask(config->instance, mb_indx, |
| data->rx_cbs[alloc].rx_info.frame, mask); |
| |
| Canexcel_Ip_ConfigRx(config->instance, mb_indx, filter->id, |
| &data->rx_cbs[alloc].rx_info); |
| |
| Canexcel_Ip_ReceiveFD(config->instance, mb_indx, &data->rx_msg[alloc], FALSE); |
| #endif |
| |
| Canexcel_Ip_ExitFreezeMode(config->instance); |
| |
| unlock: |
| k_mutex_unlock(&data->rx_mutex); |
| |
| return alloc; |
| } |
| |
| static int can_nxp_s32_send(const struct device *dev, |
| const struct can_frame *frame, |
| k_timeout_t timeout, |
| can_tx_callback_t callback, void *user_data) |
| { |
| const struct can_nxp_s32_config *config = dev->config; |
| uint8_t data_length = can_dlc_to_bytes(frame->dlc); |
| struct can_nxp_s32_data *data = dev->data; |
| Canexcel_Ip_StatusType status; |
| enum can_state state; |
| int alloc, mb_indx; |
| |
| #ifdef CAN_NXP_S32_FD_MODE |
| if ((frame->flags & ~(CAN_FRAME_IDE | CAN_FRAME_FDF | CAN_FRAME_BRS)) != 0) { |
| LOG_ERR("unsupported CAN frame flags 0x%02x", frame->flags); |
| return -ENOTSUP; |
| } |
| |
| if ((frame->flags & CAN_FRAME_FDF) != 0 && |
| (config->base_sic->BCFG2 & CANXL_SIC_BCFG2_FDEN_MASK) == 0) { |
| LOG_ERR("CAN FD format not supported in non-FD mode"); |
| return -ENOTSUP; |
| } |
| |
| if ((frame->flags & CAN_FRAME_BRS) != 0 && |
| ~(config->base_sic->BCFG1 & CANXL_SIC_BCFG1_FDRSDIS_MASK) == 0) { |
| LOG_ERR("CAN FD BRS not supported in non-FD mode"); |
| return -ENOTSUP; |
| } |
| #else |
| if ((frame->flags & ~CAN_FRAME_IDE) != 0) { |
| LOG_ERR("unsupported CAN frame flags 0x%02x", frame->flags); |
| return -ENOTSUP; |
| } |
| #endif |
| |
| if (data_length > sizeof(frame->data)) { |
| LOG_ERR("data length (%d) > max frame data length (%d)", |
| data_length, sizeof(frame->data)); |
| return -EINVAL; |
| } |
| |
| if ((frame->flags & CAN_FRAME_FDF) == 0) { |
| if (frame->dlc > CAN_MAX_DLC) { |
| LOG_ERR("DLC of %d for non-FD format frame", frame->dlc); |
| return -EINVAL; |
| } |
| #ifdef CAN_NXP_S32_FD_MODE |
| } else { |
| if (frame->dlc > CANFD_MAX_DLC) { |
| LOG_ERR("DLC of %d for CAN FD format frame", frame->dlc); |
| return -EINVAL; |
| } |
| #endif |
| } |
| |
| if (!data->common.started) { |
| return -ENETDOWN; |
| } |
| |
| can_nxp_s32_get_state(dev, &state, NULL); |
| if (state == CAN_STATE_BUS_OFF) { |
| LOG_ERR("Transmit failed, bus-off"); |
| return -ENETUNREACH; |
| } |
| |
| if (k_sem_take(&data->tx_allocs_sem, timeout) != 0) { |
| return -EAGAIN; |
| } |
| |
| for (alloc = 0; alloc < CONFIG_CAN_NXP_S32_MAX_TX; alloc++) { |
| if (!atomic_test_and_set_bit(data->tx_allocs, alloc)) { |
| break; |
| } |
| } |
| |
| data->tx_cbs[alloc].function = callback; |
| data->tx_cbs[alloc].arg = user_data; |
| mb_indx = ALLOC_IDX_TO_TXMB_IDX(alloc); |
| data->tx_cbs[alloc].tx_info = (Canexcel_Ip_DataInfoType) { |
| .frame = !!(frame->flags & CAN_FRAME_FDF) ? |
| CANEXCEL_FD_FRAME : CANEXCEL_CLASIC_FRAME, |
| .enable_brs = !!(frame->flags & CAN_FRAME_BRS) ? TRUE : FALSE, |
| .idType = !!(frame->flags & CAN_FRAME_IDE) ? |
| CANEXCEL_MSG_ID_EXT : CANEXCEL_MSG_ID_STD, |
| .priority = 0, |
| .fd_padding = 0, |
| .dataLength = data_length, |
| .is_polling = FALSE |
| }; |
| |
| LOG_DBG("%s: Sending %d bytes Tx Mb %d, " |
| "Tx Id: 0x%x, " |
| "Id type: %s %s %s %s", |
| dev->name, data_length, |
| mb_indx, frame->id, |
| !!(frame->flags & CAN_FRAME_IDE) ? |
| "extended" : "standard", |
| !!(frame->flags & CAN_FRAME_RTR) ? "RTR" : "", |
| !!(frame->flags & CAN_FRAME_FDF) ? "FD frame" : "", |
| !!(frame->flags & CAN_FRAME_BRS) ? "BRS" : ""); |
| |
| k_mutex_lock(&data->tx_mutex, K_FOREVER); |
| /* Send MB Interrupt */ |
| status = Canexcel_Ip_SendFDMsg(config->instance, mb_indx, &data->tx_cbs[alloc].tx_info, |
| frame->id, (uint8_t *)&frame->data, &data->tx_msg[alloc]); |
| k_mutex_unlock(&data->tx_mutex); |
| |
| if (status != CANEXCEL_STATUS_SUCCESS) { |
| return -EIO; |
| } |
| |
| return 0; |
| } |
| |
| static void nxp_s32_zcan_timing_to_canxl_timing(const struct can_timing *timing, |
| Canexcel_Ip_TimeSegmentType *canxl_timing) |
| { |
| LOG_DBG("propSeg: %d, phase_seg1: %d, phase_seg2: %d, prescaler: %d, sjw: %d", |
| timing->prop_seg, timing->phase_seg1, timing->phase_seg2, |
| timing->prescaler, timing->sjw); |
| |
| canxl_timing->propSeg = timing->prop_seg - 1U; |
| canxl_timing->phaseSeg1 = timing->phase_seg1 - 1U; |
| canxl_timing->phaseSeg2 = timing->phase_seg2 - 1U; |
| canxl_timing->preDivider = timing->prescaler - 1U; |
| canxl_timing->rJumpwidth = timing->sjw - 1U; |
| } |
| |
| static int can_nxp_s32_set_timing(const struct device *dev, |
| const struct can_timing *timing) |
| { |
| const struct can_nxp_s32_config *config = dev->config; |
| struct can_nxp_s32_data *data = dev->data; |
| Canexcel_Ip_TimeSegmentType can_time_segment = {0}; |
| |
| if (data->common.started) { |
| return -EBUSY; |
| } |
| |
| nxp_s32_zcan_timing_to_canxl_timing(timing, &can_time_segment); |
| |
| /* Set timing for CAN instance*/ |
| CanXL_SetBaudRate(config->base_sic, &can_time_segment); |
| |
| return 0; |
| } |
| |
| #ifdef CAN_NXP_S32_FD_MODE |
| static int can_nxp_s32_set_timing_data(const struct device *dev, |
| const struct can_timing *timing_data) |
| { |
| const struct can_nxp_s32_config *config = dev->config; |
| struct can_nxp_s32_data *data = dev->data; |
| Canexcel_Ip_TimeSegmentType can_fd_time_segment = {0}; |
| |
| if (data->common.started) { |
| return -EBUSY; |
| } |
| |
| nxp_s32_zcan_timing_to_canxl_timing(timing_data, &can_fd_time_segment); |
| |
| /* Set timing for CAN FD instance*/ |
| CanXL_SetFDBaudRate(config->base_sic, &can_fd_time_segment); |
| |
| Canexcel_Ip_SetTDCOffsetFD(config->instance, true, false, |
| CAN_CALC_TDCO((timing_data), 0U, CAN_NXP_S32_TDCO_MAX)); |
| |
| return 0; |
| } |
| #endif |
| |
| static void can_nxp_s32_err_callback(const struct device *dev, |
| Canexcel_Ip_EventType eventType, |
| uint32_t u32SysStatus, |
| const Canexcel_Ip_StateType *canexcelState) |
| { |
| const struct can_nxp_s32_config *config = dev->config; |
| struct can_nxp_s32_data *data = dev->data; |
| enum can_state state; |
| struct can_bus_err_cnt err_cnt; |
| void *cb_data = data->common.state_change_cb_user_data; |
| can_tx_callback_t function; |
| int alloc; |
| void *arg; |
| |
| switch (eventType) { |
| case CANEXCEL_EVENT_TX_WARNING: |
| LOG_DBG("Tx Warning (error 0x%x)", u32SysStatus); |
| break; |
| case CANEXCEL_EVENT_RX_WARNING: |
| LOG_DBG("Rx Warning (error 0x%x)", u32SysStatus); |
| break; |
| case CANEXCEL_EVENT_BUSOFF: |
| LOG_DBG("Bus Off (error 0x%x)", u32SysStatus); |
| break; |
| case CANEXCEL_EVENT_ERROR: |
| LOG_DBG("Error Format Frames (error 0x%x)", u32SysStatus); |
| break; |
| case CANEXCEL_EVENT_ERROR_FD: |
| LOG_DBG("Error Data Phase (error 0x%x)", u32SysStatus); |
| break; |
| case CANEXCEL_EVENT_PASSIVE: |
| LOG_DBG("Error Passive (error 0x%x)", u32SysStatus); |
| break; |
| default: |
| break; |
| } |
| |
| if (u32SysStatus & BCANXL_ST_BIT0ERR_MASK) { |
| CAN_STATS_BIT0_ERROR_INC(dev); |
| } |
| if (u32SysStatus & BCANXL_ST_BIT1ERR_MASK) { |
| CAN_STATS_BIT1_ERROR_INC(dev); |
| } |
| if (u32SysStatus & BCANXL_ST_ACKERR_MASK) { |
| CAN_STATS_ACK_ERROR_INC(dev); |
| } |
| if (u32SysStatus & BCANXL_ST_STFERR_MASK) { |
| CAN_STATS_STUFF_ERROR_INC(dev); |
| } |
| if (u32SysStatus & BCANXL_ST_FRMERR_MASK) { |
| CAN_STATS_FORM_ERROR_INC(dev); |
| } |
| if (u32SysStatus & BCANXL_ST_CRCERR_MASK) { |
| CAN_STATS_CRC_ERROR_INC(dev); |
| } |
| if (u32SysStatus & BCANXL_ST_OVRERR_MASK) { |
| CAN_STATS_RX_OVERRUN_INC(dev); |
| } |
| |
| can_nxp_s32_get_state(dev, &state, &err_cnt); |
| if (data->state != state) { |
| data->state = state; |
| if (data->common.state_change_cb) { |
| data->common.state_change_cb(dev, state, err_cnt, cb_data); |
| } |
| } |
| |
| if (state == CAN_STATE_BUS_OFF) { |
| /* Abort any pending TX frames in case of bus-off */ |
| for (alloc = 0; alloc < CONFIG_CAN_NXP_S32_MAX_TX; alloc++) { |
| /* Copy callback function and argument before clearing bit */ |
| function = data->tx_cbs[alloc].function; |
| arg = data->tx_cbs[alloc].arg; |
| |
| if (atomic_test_and_clear_bit(data->tx_allocs, alloc)) { |
| if (Canexcel_Ip_DeactivateMD(config->instance, |
| ALLOC_IDX_TO_TXMB_IDX(alloc))) { |
| LOG_ERR("Can't abort message !"); |
| }; |
| |
| function(dev, -ENETUNREACH, arg); |
| k_sem_give(&data->tx_allocs_sem); |
| } |
| } |
| } |
| } |
| |
| static void nxp_s32_msg_data_to_zcan_frame(Canexcel_RxFdMsg msg_data, |
| struct can_frame *frame) |
| { |
| memset(frame, 0, sizeof(*frame)); |
| |
| if (!!(msg_data.Header.Id & CANXL_TX_HEADER_IDE_MASK)) { |
| frame->flags |= CAN_FRAME_IDE; |
| } |
| |
| if (!!(frame->flags & CAN_FRAME_IDE)) { |
| frame->id = (msg_data.Header.Id & CANXL_IP_ID_EXT_MASK); |
| } else { |
| frame->id = ((msg_data.Header.Id & CANXL_IP_ID_STD_MASK) |
| >> CANXL_IP_ID_STD_SHIFT); |
| } |
| |
| frame->dlc = (msg_data.Header.Control & CANXL_TX_HEADER_DLC_MASK) |
| >> CANXL_TX_HEADER_DLC_SHIFT; |
| |
| if (!!(msg_data.Header.Control & CANXL_TX_HEADER_FDF_MASK)) { |
| frame->flags |= CAN_FRAME_FDF; |
| } |
| |
| if (!!(msg_data.Header.Control & CANXL_TX_HEADER_BRS_MASK)) { |
| frame->flags |= CAN_FRAME_BRS; |
| } |
| |
| if (!!(msg_data.Header.Id & CANXL_TX_HEADER_RTR_MASK)) { |
| frame->flags |= CAN_FRAME_RTR; |
| } else { |
| memcpy(frame->data, msg_data.data, can_dlc_to_bytes(frame->dlc)); |
| } |
| |
| #ifdef CONFIG_CAN_RX_TIMESTAMP |
| frame->timestamp = msg_data.timeStampL; |
| #endif /* CAN_RX_TIMESTAMP */ |
| } |
| |
| static void can_nxp_s32_ctrl_callback(const struct device *dev, |
| Canexcel_Ip_EventType eventType, uint32_t buffidx, |
| const Canexcel_Ip_StateType *canexcelState) |
| { |
| const struct can_nxp_s32_config *config = dev->config; |
| struct can_nxp_s32_data *data = dev->data; |
| struct can_frame frame = {0}; |
| can_tx_callback_t tx_func; |
| can_rx_callback_t rx_func; |
| int alloc; |
| |
| if (eventType == CANEXCEL_EVENT_TX_COMPLETE) { |
| alloc = TX_MBIDX_TO_ALLOC_IDX(buffidx); |
| tx_func = data->tx_cbs[alloc].function; |
| LOG_DBG("%s: Sent Tx Mb %d", dev->name, buffidx); |
| if (atomic_test_and_clear_bit(data->tx_allocs, alloc)) { |
| tx_func(dev, 0, data->tx_cbs[alloc].arg); |
| k_sem_give(&data->tx_allocs_sem); |
| } |
| #ifdef CONFIG_CAN_NXP_S32_RX_FIFO |
| } else if (eventType == CANEXCEL_EVENT_RXFIFO_COMPLETE) { |
| uint8_t queue_idx = ((config->base_rx_fifo_ctrl->RXFCSTA & |
| CANXL_RXFIFO_CONTROL_RXFCSTA_HWPOINTER_MASK) >> |
| CANXL_RXFIFO_CONTROL_RXFCSTA_HWPOINTER_SHIFT) - |
| 1; |
| |
| alloc = can_nxp_s32_get_rx_fifo_filter(data, queue_idx); |
| |
| if (alloc != -ENOSPC) { |
| rx_func = data->rx_cbs[alloc].function; |
| if (atomic_test_bit(data->rx_allocs, alloc)) { |
| nxp_s32_msg_data_to_zcan_frame(data->rx_fifo[queue_idx], &frame); |
| |
| LOG_DBG("%s: Received %d bytes Rx FiFo %d, " |
| "Rx Id: 0x%x, " |
| "Id type: %s %s %s %s", |
| dev->name, can_dlc_to_bytes(frame.dlc), |
| alloc, frame.id, |
| !!(frame.flags & CAN_FRAME_IDE) ? |
| "extended" : "standard", |
| !!(frame.flags & CAN_FRAME_RTR) ? "RTR" : "", |
| !!(frame.flags & CAN_FRAME_FDF) ? "FD frame" : "", |
| !!(frame.flags & CAN_FRAME_BRS) ? "BRS" : ""); |
| |
| rx_func(dev, &frame, data->rx_cbs[alloc].arg); |
| } |
| } |
| |
| /* Pop 1 (= RXFSYSPOP + 1) received RxFIFO message queue */ |
| config->base_rx_fifo_ctrl->RXFSYSPOP = 0; |
| #else |
| } else if (eventType == CANEXCEL_EVENT_RX_COMPLETE) { |
| alloc = RX_MBIDX_TO_ALLOC_IDX(buffidx); |
| rx_func = data->rx_cbs[alloc].function; |
| if (atomic_test_bit(data->rx_allocs, alloc)) { |
| nxp_s32_msg_data_to_zcan_frame(data->rx_msg[alloc], &frame); |
| |
| LOG_DBG("%s: Received %d bytes Rx Mb %d, " |
| "Rx Id: 0x%x, " |
| "Id type: %s %s %s %s", |
| dev->name, can_dlc_to_bytes(frame.dlc), |
| buffidx, frame.id, |
| !!(frame.flags & CAN_FRAME_IDE) ? |
| "extended" : "standard", |
| !!(frame.flags & CAN_FRAME_RTR) ? "RTR" : "", |
| !!(frame.flags & CAN_FRAME_FDF) ? "FD frame" : "", |
| !!(frame.flags & CAN_FRAME_BRS) ? "BRS" : ""); |
| |
| rx_func(dev, &frame, data->rx_cbs[alloc].arg); |
| |
| if (Canexcel_Ip_ReceiveFD(config->instance, buffidx, |
| &data->rx_msg[alloc], FALSE) != CANEXCEL_STATUS_SUCCESS) { |
| LOG_ERR("MB %d is not ready for receiving next message", buffidx); |
| } |
| } |
| #endif |
| } |
| } |
| |
| static int can_nxp_s32_init(const struct device *dev) |
| { |
| const struct can_nxp_s32_config *config = dev->config; |
| struct can_nxp_s32_data *data = dev->data; |
| int err; |
| #ifdef CONFIG_CAN_RX_TIMESTAMP |
| Canexcel_Ip_TimeStampConf_Type time_stamp = { |
| .ts64bit = FALSE, /* Time stamp size is 32 bits */ |
| .capture = CANEXCEL_TIMESTAMPCAPTURE_END, |
| .src = CANTBS_TIMESURCE_BUS1 |
| }; |
| #endif |
| |
| if (config->common.phy != NULL) { |
| if (!device_is_ready(config->common.phy)) { |
| LOG_ERR("CAN transceiver not ready"); |
| return -ENODEV; |
| } |
| } |
| |
| if (!device_is_ready(config->clock_dev)) { |
| LOG_ERR("Clock control device not ready"); |
| return -ENODEV; |
| } |
| |
| err = clock_control_on(config->clock_dev, config->clock_subsys); |
| if (err) { |
| LOG_ERR("Failed to enable clock"); |
| return err; |
| } |
| |
| k_mutex_init(&data->rx_mutex); |
| k_mutex_init(&data->tx_mutex); |
| k_sem_init(&data->tx_allocs_sem, CONFIG_CAN_NXP_S32_MAX_TX, CONFIG_CAN_NXP_S32_MAX_TX); |
| |
| err = pinctrl_apply_state(config->pin_cfg, PINCTRL_STATE_DEFAULT); |
| if (err < 0) { |
| return err; |
| } |
| |
| /* Enable CANXL HW */ |
| IP_MC_RGM->PRST_0[0].PRST_0 &= |
| ~(MC_RGM_PRST_0_PERIPH_16_RST_MASK | MC_RGM_PRST_0_PERIPH_24_RST_MASK); |
| |
| err = can_calc_timing(dev, &data->timing, config->common.bitrate, |
| config->common.sample_point); |
| if (err == -EINVAL) { |
| LOG_ERR("Can't find timing for given param"); |
| return -EIO; |
| } |
| |
| if (err > 0) { |
| LOG_WRN("Sample-point error : %d", err); |
| } |
| |
| LOG_DBG("Setting CAN bitrate %d:", config->common.bitrate); |
| nxp_s32_zcan_timing_to_canxl_timing(&data->timing, &config->can_cfg->bitrate); |
| |
| #ifdef CAN_NXP_S32_FD_MODE |
| err = can_calc_timing_data(dev, &data->timing_data, config->common.bitrate_data, |
| config->common.sample_point_data); |
| if (err == -EINVAL) { |
| LOG_ERR("Can't find timing data for given param"); |
| return -EIO; |
| } |
| |
| if (err > 0) { |
| LOG_WRN("Sample-point-data err : %d", err); |
| } |
| |
| LOG_DBG("Setting CAN FD bitrate %d:", config->common.bitrate_data); |
| nxp_s32_zcan_timing_to_canxl_timing(&data->timing_data, &config->can_cfg->Fd_bitrate); |
| #endif |
| |
| /* Initialize CAN structure */ |
| Canexcel_Ip_Init(config->instance, config->can_cfg, data->can_state); |
| |
| #ifdef CAN_NXP_S32_FD_MODE |
| Canexcel_Ip_SetTDCOffsetFD(config->instance, true, false, |
| CAN_CALC_TDCO((&data->timing_data), 0U, CAN_NXP_S32_TDCO_MAX)); |
| #endif |
| |
| /* Configure time stamp */ |
| #ifdef CONFIG_CAN_RX_TIMESTAMP |
| Canexcel_Ip_ConfigTimeStamp(config->instance, &time_stamp); |
| #endif |
| |
| /* Enable Interrupt */ |
| Canexcel_Ip_EnableInterrupts(config->instance); |
| |
| /* Enable Error Interrupt */ |
| CanXL_SetErrIntCmd(config->base_sic, CANXL_INT_RX_WARNING, TRUE); |
| CanXL_SetErrIntCmd(config->base_sic, CANXL_INT_TX_WARNING, TRUE); |
| CanXL_SetErrIntCmd(config->base_sic, CANXL_INT_ERR, TRUE); |
| CanXL_SetErrIntCmd(config->base_sic, CANXL_INT_BUSOFF, TRUE); |
| CanXL_SetErrIntCmd(config->base_sic, CANXL_INT_PASIVE_ERR, TRUE); |
| CanXL_SetErrIntCmd(config->base_sic, CANXL_INT_RXSMB_OVER, TRUE); |
| #ifdef CAN_NXP_S32_FD_MODE |
| CanXL_SetErrIntCmd(config->base_sic, CANXL_INT_ERR_FAST, TRUE); |
| #endif |
| #ifdef CONFIG_CAN_NXP_S32_RX_FIFO |
| CanXL_SetErrIntCmd(config->base_sic, CANXL_INT_RXFIFO_OVER, TRUE); |
| |
| /* Configure number of ID acceptance filters*/ |
| config->base_rx_fifo->AFCFG = |
| CANXL_RXFIFO_AFCFG_ACPTID(CONFIG_CAN_NXP_S32_MAX_RX - 1); |
| #endif |
| |
| config->irq_config_func(); |
| |
| can_nxp_s32_get_state(dev, &data->state, NULL); |
| |
| return 0; |
| } |
| |
| static void can_nxp_s32_isr_rx_tx_mru(const struct device *dev) |
| { |
| const struct can_nxp_s32_config *config = dev->config; |
| |
| Canexcel_Ip_RxTxIRQHandler(config->instance); |
| |
| if ((config->base_mru->CHXCONFIG[0u].CH_CFG0 & CANXL_MRU_CH_CFG0_CHE_MASK) |
| == CANXL_MRU_CH_CFG0_CHE_MASK) { |
| Canexcel_Ip_MruIRQHandler(config->instance); |
| } |
| } |
| |
| static void can_nxp_s32_isr_error(const struct device *dev) |
| { |
| const struct can_nxp_s32_config *config = dev->config; |
| |
| Canexcel_Ip_ErrIRQHandler(config->instance); |
| } |
| |
| static const struct can_driver_api can_nxp_s32_driver_api = { |
| .get_capabilities = can_nxp_s32_get_capabilities, |
| .start = can_nxp_s32_start, |
| .stop = can_nxp_s32_stop, |
| .set_mode = can_nxp_s32_set_mode, |
| .set_timing = can_nxp_s32_set_timing, |
| .send = can_nxp_s32_send, |
| .add_rx_filter = can_nxp_s32_add_rx_filter, |
| .remove_rx_filter = can_nxp_s32_remove_rx_filter, |
| .get_state = can_nxp_s32_get_state, |
| #ifdef CONFIG_CAN_MANUAL_RECOVERY_MODE |
| .recover = can_nxp_s32_recover, |
| #endif /* CONFIG_CAN_MANUAL_RECOVERY_MODE */ |
| .set_state_change_callback = can_nxp_s32_set_state_change_callback, |
| .get_core_clock = can_nxp_s32_get_core_clock, |
| .get_max_filters = can_nxp_s32_get_max_filters, |
| .timing_min = { |
| .sjw = 0x01, |
| .prop_seg = 0x01, |
| .phase_seg1 = 0x01, |
| .phase_seg2 = 0x02, |
| .prescaler = 0x01 |
| }, |
| .timing_max = { |
| .sjw = 0x04, |
| .prop_seg = 0x08, |
| .phase_seg1 = 0x08, |
| .phase_seg2 = 0x08, |
| .prescaler = 0x100 |
| }, |
| #ifdef CAN_NXP_S32_FD_MODE |
| .set_timing_data = can_nxp_s32_set_timing_data, |
| .timing_data_min = { |
| .sjw = 0x01, |
| .prop_seg = 0x01, |
| .phase_seg1 = 0x01, |
| .phase_seg2 = 0x02, |
| .prescaler = 0x01 |
| }, |
| .timing_data_max = { |
| .sjw = 0x04, |
| .prop_seg = 0x08, |
| .phase_seg1 = 0x08, |
| .phase_seg2 = 0x08, |
| .prescaler = 0x100 |
| } |
| #endif |
| }; |
| |
| #define _CAN_NXP_S32_IRQ_CONFIG(node_id, prop, idx) \ |
| do { \ |
| IRQ_CONNECT(DT_IRQ_BY_IDX(node_id, idx, irq), \ |
| DT_IRQ_BY_IDX(node_id, idx, priority), \ |
| UTIL_CAT(can_nxp_s32_isr_, \ |
| DT_STRING_TOKEN_BY_IDX(node_id, prop, idx)), \ |
| DEVICE_DT_GET(node_id), \ |
| DT_IRQ_BY_IDX(node_id, idx, flags)); \ |
| irq_enable(DT_IRQ_BY_IDX(node_id, idx, irq)); \ |
| } while (false); |
| |
| #define CAN_NXP_S32_IRQ_CONFIG(n) \ |
| static void can_irq_config_##n(void) \ |
| { \ |
| DT_INST_FOREACH_PROP_ELEM(n, interrupt_names, _CAN_NXP_S32_IRQ_CONFIG); \ |
| } |
| |
| #define CAN_NXP_S32_ERR_CALLBACK(n) \ |
| void nxp_s32_can_##n##_err_callback(uint8 instance, Canexcel_Ip_EventType eventType,\ |
| uint32 u32SysStatus, const Canexcel_Ip_StateType *canexcelState) \ |
| { \ |
| const struct device *dev = DEVICE_DT_INST_GET(n); \ |
| can_nxp_s32_err_callback(dev, eventType, u32SysStatus, canexcelState); \ |
| } |
| |
| #define CAN_NXP_S32_CTRL_CALLBACK(n) \ |
| void nxp_s32_can_##n##_ctrl_callback(uint8 instance, Canexcel_Ip_EventType eventType,\ |
| uint32 buffIdx, const Canexcel_Ip_StateType *canexcelState) \ |
| { \ |
| const struct device *dev = DEVICE_DT_INST_GET(n); \ |
| can_nxp_s32_ctrl_callback(dev, eventType, buffIdx, canexcelState); \ |
| } |
| |
| #if defined(CAN_NXP_S32_FD_MODE) |
| #define CAN_NXP_S32_BRS 1 |
| #else |
| #define CAN_NXP_S32_BRS 0 |
| #endif |
| |
| #define CAN_NXP_S32_HW_INSTANCE_CHECK(i, n) \ |
| ((DT_INST_REG_ADDR(n) == IP_CANXL_##i##__SIC_BASE) ? i : 0) |
| |
| #define CAN_NXP_S32_HW_INSTANCE(n) \ |
| LISTIFY(__DEBRACKET CANXL_SIC_INSTANCE_COUNT, CAN_NXP_S32_HW_INSTANCE_CHECK, (|), n) |
| |
| #define CAN_NXP_S32_INIT_DEVICE(n) \ |
| CAN_NXP_S32_CTRL_CALLBACK(n) \ |
| CAN_NXP_S32_ERR_CALLBACK(n) \ |
| CAN_NXP_S32_IRQ_CONFIG(n) \ |
| PINCTRL_DT_INST_DEFINE(n); \ |
| \ |
| __nocache Canexcel_Ip_StateType can_nxp_s32_state##n; \ |
| __nocache Canexcel_TxFdMsgType tx_msg##n[CONFIG_CAN_NXP_S32_MAX_TX]; \ |
| IF_DISABLED(CONFIG_CAN_NXP_S32_RX_FIFO, \ |
| (__nocache Canexcel_RxFdMsg rx_msg_##n[CONFIG_CAN_NXP_S32_MAX_RX];)) \ |
| IF_ENABLED(CONFIG_CAN_NXP_S32_RX_FIFO, \ |
| (__nocache Canexcel_RxFdMsg rx_fifo_##n[CAN_NXP_S32_RX_FIFO_DEPTH]; \ |
| static Canexcel_Ip_RxFifoFilterID_ADDR \ |
| rx_fifo_filter##n[CONFIG_CAN_NXP_S32_MAX_RX];)) \ |
| Canexcel_Ip_ConfigType can_nxp_s32_default_config##n = { \ |
| .rx_mbdesc = (uint8)IS_ENABLED(CONFIG_CAN_NXP_S32_RX_FIFO) ? \ |
| 0 : CONFIG_CAN_NXP_S32_MAX_RX, \ |
| .tx_mbdesc = (uint8)CONFIG_CAN_NXP_S32_MAX_TX, \ |
| .CanxlMode = CANEXCEL_LISTEN_ONLY_MODE, \ |
| .fd_enable = (boolean)IS_ENABLED(CAN_NXP_S32_FD_MODE), \ |
| .bitRateSwitch = (boolean)CAN_NXP_S32_BRS, \ |
| .ctrlOptions = CANXL_IP_BUSOFF_RECOVERY_U32, \ |
| .Callback = nxp_s32_can_##n##_ctrl_callback, \ |
| .ErrorCallback = nxp_s32_can_##n##_err_callback, \ |
| IF_ENABLED(CONFIG_CAN_NXP_S32_RX_FIFO, \ |
| (.is_rx_fifo_needed = (boolean)TRUE, \ |
| .pRxFifoConfig = { \ |
| .Rx_Fifo_Depth = CAN_NXP_S32_RX_FIFO_DEPTH, \ |
| .Rx_Fifo_Watermark = CAN_NXP_S32_RX_FIFO_WATERMARK, \ |
| .Rx_Fifo_Msg_Size = CAN_NXP_S32_DATA_LENGTH, \ |
| .Rx_Fifo_KeepLast = (boolean)FALSE, \ |
| .isPolling = (boolean)FALSE, \ |
| .MsgBuffersPtr = (uint32 *)rx_fifo_##n, \ |
| },)) \ |
| }; \ |
| static struct can_nxp_s32_data can_nxp_s32_data_##n = { \ |
| .can_state = (Canexcel_Ip_StateType *)&can_nxp_s32_state##n, \ |
| .tx_msg = tx_msg##n, \ |
| IF_DISABLED(CONFIG_CAN_NXP_S32_RX_FIFO, \ |
| (.rx_msg = rx_msg_##n,)) \ |
| IF_ENABLED(CONFIG_CAN_NXP_S32_RX_FIFO, \ |
| (.rx_fifo = rx_fifo_##n, \ |
| .rx_fifo_filter = \ |
| (Canexcel_Ip_RxFifoFilterID_ADDR *)&rx_fifo_filter##n,))\ |
| }; \ |
| static struct can_nxp_s32_config can_nxp_s32_config_##n = { \ |
| .common = CAN_DT_DRIVER_CONFIG_INST_GET(n, 0, CAN_NXP_S32_MAX_BITRATE), \ |
| .base_sic = (CANXL_SIC_Type *)DT_INST_REG_ADDR_BY_NAME(n, sic), \ |
| IF_ENABLED(CONFIG_CAN_NXP_S32_RX_FIFO, \ |
| (.base_rx_fifo = (CANXL_RXFIFO_Type *) \ |
| DT_INST_REG_ADDR_BY_NAME(n, rx_fifo), \ |
| .base_rx_fifo_ctrl = (CANXL_RXFIFO_CONTROL_Type *) \ |
| DT_INST_REG_ADDR_BY_NAME(n, rx_fifo_ctrl),)) \ |
| .base_mru = (CANXL_MRU_Type *)DT_INST_REG_ADDR_BY_NAME(n, mru), \ |
| .instance = CAN_NXP_S32_HW_INSTANCE(n), \ |
| .clock_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(n)), \ |
| .clock_subsys = (clock_control_subsys_t) \ |
| DT_INST_CLOCKS_CELL(n, name), \ |
| .pin_cfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \ |
| .can_cfg = (Canexcel_Ip_ConfigType *)&can_nxp_s32_default_config##n, \ |
| .irq_config_func = can_irq_config_##n \ |
| }; \ |
| static int can_nxp_s32_##n##_init(const struct device *dev) \ |
| { \ |
| return can_nxp_s32_init(dev); \ |
| } \ |
| CAN_DEVICE_DT_INST_DEFINE(n, \ |
| can_nxp_s32_##n##_init, \ |
| NULL, \ |
| &can_nxp_s32_data_##n, \ |
| &can_nxp_s32_config_##n, \ |
| POST_KERNEL, \ |
| CONFIG_CAN_INIT_PRIORITY, \ |
| &can_nxp_s32_driver_api); |
| |
| DT_INST_FOREACH_STATUS_OKAY(CAN_NXP_S32_INIT_DEVICE) |