/* * Copyright 2022-2023 NXP * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include #include #include #include #include #include #include #include #define DT_DRV_COMPAT nxp_s32_canxl /* * Convert from RX message buffer index to allocated filter ID and * vice versa. */ #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) /* * 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 LOG_MODULE_REGISTER(nxp_s32_canxl, CONFIG_CAN_LOG_LEVEL); #define SP_AND_TIMING_NOT_SET(inst) \ (!DT_INST_NODE_HAS_PROP(inst, sample_point) && \ !(DT_INST_NODE_HAS_PROP(inst, prop_seg) && \ DT_INST_NODE_HAS_PROP(inst, phase_seg1) && \ DT_INST_NODE_HAS_PROP(inst, phase_seg2))) || #if DT_INST_FOREACH_STATUS_OKAY(SP_AND_TIMING_NOT_SET) 0 #error You must either set a sampling-point or timings (phase-seg* and prop-seg) #endif #ifdef CONFIG_CAN_FD_MODE #define SP_AND_TIMING_DATA_NOT_SET(inst) \ (!DT_INST_NODE_HAS_PROP(inst, sample_point_data) && \ !(DT_INST_NODE_HAS_PROP(inst, prop_seg_data) && \ DT_INST_NODE_HAS_PROP(inst, phase_seg1_data) && \ DT_INST_NODE_HAS_PROP(inst, phase_seg2_data))) || #if DT_INST_FOREACH_STATUS_OKAY(SP_AND_TIMING_DATA_NOT_SET) 0 #error You must either set a sampling-point-data or timings (phase-seg-data* and prop-seg-data) #endif #endif struct can_nxp_s32_config { CANXL_SIC_Type *base_sic; CANXL_GRP_CONTROL_Type *base_grp_ctrl; CANXL_DSC_CONTROL_Type *base_dsc_ctrl; uint8 instance; const struct device *clock_dev; clock_control_subsys_t clock_subsys; uint32_t bitrate; uint32_t sample_point; uint32_t sjw; uint32_t prop_seg; uint32_t phase_seg1; uint32_t phase_seg2; #ifdef CONFIG_CAN_FD_MODE uint32_t bitrate_data; uint32_t sample_point_data; uint32_t sjw_data; uint32_t prop_seg_data; uint32_t phase_seg1_data; uint32_t phase_seg2_data; #endif uint32_t max_bitrate; const struct device *phy; 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; Canexcel_Ip_DataInfoType rx_info; can_rx_callback_t function; void *arg; }; struct can_nxp_s32_data { 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]; Canexcel_RxFdMsg *rx_msg; 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; struct can_timing timing; #ifdef CONFIG_CAN_FD_MODE struct can_timing timing_data; #endif enum can_state state; can_state_change_callback_t state_change_cb; void *state_change_cb_data; bool started; }; 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 CONFIG_CAN_FD_MODE *cap |= CAN_MODE_FD; #endif return 0; } 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->started) { return -EALREADY; } if (config->phy != NULL) { err = can_transceiver_enable(config->phy); if (err != 0) { LOG_ERR("failed to enable CAN transceiver (err %d)", err); return err; } } data->started = true; return 0; } static int can_nxp_s32_abort_msg(const struct can_nxp_s32_config *config, int mb_idx) { uint32_t time_start = 0; int ret = 0; Canexcel_Ip_EnterFreezeMode(config->instance); CanXL_ClearMsgBuffIntCmd(config->base_grp_ctrl, mb_idx); CanXL_ClearMsgDescIntStatusFlag(config->base_grp_ctrl, mb_idx); time_start = k_uptime_get(); /* Set system lock Status */ (void)config->base_dsc_ctrl->DSCMBCTRLAR[mb_idx].SYSLOCK.DCSYSLOCK; while (CanXL_GetDescControlStatus(config->base_dsc_ctrl, mb_idx) == CANEXCEL_DESCNTSTATUS_LOCKED_HW) { if (k_uptime_get() - time_start >= CAN_NXP_S32_TIMEOUT_MS) { ret = CANEXCEL_STATUS_TIMEOUT; break; } } /* Inactive descriptor */ config->base_dsc_ctrl->DSCMBCTRLAR[mb_idx].ACT.DCACT = 0; Canexcel_Ip_ExitFreezeMode(config->instance); return ret; } 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->started) { return -EALREADY; } data->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 (can_nxp_s32_abort_msg(config, 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->phy != NULL) { err = can_transceiver_disable(config->phy); if (err != 0) { LOG_ERR("failed to disable CAN transceiver (err %d)", err); return err; } } return 0; } static int can_nxp_s32_set_mode(const struct device *dev, can_mode_t mode) { 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->started) { return -EBUSY; } #ifdef CONFIG_CAN_FD_MODE if ((mode & ~(CAN_MODE_LOOPBACK | CAN_MODE_LISTENONLY | CAN_MODE_FD)) != 0) { #else if ((mode & ~(CAN_MODE_LOOPBACK | CAN_MODE_LISTENONLY)) != 0) { #endif 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); CanXL_SetFDEnabled(config->base_sic, canfd, brs); CanXL_SetOperationMode(config->base_sic, can_nxp_s32_mode); Canexcel_Ip_ExitFreezeMode(config->instance); 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_max_bitrate(const struct device *dev, uint32_t *max_bitrate) { const struct can_nxp_s32_config *config = dev->config; *max_bitrate = config->max_bitrate; return 0; } 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; uint32_t sys_status = config->base_sic->SYSS; if (state) { if (!data->started) { *state = CAN_STATE_STOPPED; } else { if (sys_status & CANXL_SIC_SYSS_CBOFF_MASK) { *state = CAN_STATE_BUS_OFF; } else if (sys_status & CANXL_SIC_SYSS_CPASERR_MASK) { *state = CAN_STATE_ERROR_PASSIVE; } else if (sys_status & (CANXL_SIC_SYSS_CRXWRN_MASK | CANXL_SIC_SYSS_CTXWRN_MASK)) { *state = CAN_STATE_ERROR_WARNING; } else { *state = CAN_STATE_ERROR_ACTIVE; } } } if (err_cnt) { /* NXP S32 CANXL HAL is not supported error counter */ err_cnt->tx_err_cnt = 0; err_cnt->rx_err_cnt = 0; } 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->state_change_cb = callback; data->state_change_cb_data = user_data; } #ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY 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->started) { return -ENETDOWN; } 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 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); __ASSERT_NO_MSG(filter_id >= 0 && filter_id < CONFIG_CAN_NXP_S32_MAX_RX); k_mutex_lock(&data->rx_mutex, K_FOREVER); if (atomic_test_and_clear_bit(data->rx_allocs, filter_id)) { if (can_nxp_s32_abort_msg(config, mb_indx)) { LOG_ERR("Can't abort message !"); }; 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; __ASSERT_NO_MSG(callback != NULL); #if defined(CONFIG_CAN_FD_MODE) if ((filter->flags & ~(CAN_FILTER_IDE | CAN_FILTER_DATA | CAN_FILTER_FDF)) != 0) { #else if ((filter->flags & ~(CAN_FILTER_IDE | CAN_FILTER_DATA)) != 0) { #endif 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; data->rx_cbs[alloc].rx_info = (Canexcel_Ip_DataInfoType) { .frame = !!(filter->flags & CAN_FILTER_FDF) ? CANEXCEL_FD_FRAME : CANEXCEL_CLASIC_FRAME, .idType = !!(filter->flags & CAN_FILTER_IDE) ? CANEXCEL_MSG_ID_EXT : CANEXCEL_MSG_ID_STD, .dataLength = CAN_NXP_S32_DATA_LENGTH, }; /* 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); } Canexcel_Ip_EnterFreezeMode(config->instance); 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); 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; __ASSERT_NO_MSG(callback != NULL); #ifdef CONFIG_CAN_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 CONFIG_CAN_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->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->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 CONFIG_CAN_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->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); return 0; } #endif static void can_nxp_s32_err_callback(const struct device *dev, Canexcel_Ip_EventType eventType, uint32 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->state_change_cb_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; } can_nxp_s32_get_state(dev, &state, &err_cnt); if (data->state != state) { data->state = state; if (data->state_change_cb) { data->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 (can_nxp_s32_abort_msg(config, 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 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; Canexcel_Ip_StatusType status; 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); } } 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); status = Canexcel_Ip_ReceiveFD(config->instance, buffidx, &data->rx_msg[alloc], FALSE); if (status != CANEXCEL_STATUS_SUCCESS) { LOG_ERR("MB %d is not ready for receiving next message", buffidx); } } } } 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->phy != NULL) { if (!device_is_ready(config->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); data->timing.sjw = config->sjw; if (config->sample_point) { err = can_calc_timing(dev, &data->timing, config->bitrate, config->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); } } else { data->timing.prop_seg = config->prop_seg; data->timing.phase_seg1 = config->phase_seg1; data->timing.phase_seg2 = config->phase_seg2; err = can_calc_prescaler(dev, &data->timing, config->bitrate); if (err) { LOG_WRN("Bitrate error: %d", err); } } LOG_DBG("Setting CAN bitrate %d:", config->bitrate); nxp_s32_zcan_timing_to_canxl_timing(&data->timing, &config->can_cfg->bitrate); #ifdef CONFIG_CAN_FD_MODE data->timing_data.sjw = config->sjw_data; if (config->sample_point_data) { err = can_calc_timing_data(dev, &data->timing_data, config->bitrate_data, config->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); } } else { data->timing_data.prop_seg = config->prop_seg_data; data->timing_data.phase_seg1 = config->phase_seg1_data; data->timing_data.phase_seg2 = config->phase_seg2_data; err = can_calc_prescaler(dev, &data->timing_data, config->bitrate_data); if (err) { LOG_WRN("Bitrate data error: %d", err); } } LOG_DBG("Setting CAN-FD bitrate %d:", config->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); /* 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); config->irq_config_func(); can_nxp_s32_get_state(dev, &data->state, NULL); return 0; } 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, #ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY .recover = can_nxp_s32_recover, #endif .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, .get_max_bitrate = can_nxp_s32_get_max_bitrate, .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 CONFIG_CAN_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_NODE(n) DT_NODELABEL(can##n) #define CAN_NXP_S32_IRQ_HANDLER(n, irq_name) DT_CAT5(CANXL, n, _, irq_name, Handler) #define _CAN_NXP_S32_IRQ_CONFIG(node_id, prop, idx, n) \ do { \ extern void (CAN_NXP_S32_IRQ_HANDLER(n, \ DT_STRING_TOKEN_BY_IDX(node_id, prop, idx)))(void); \ IRQ_CONNECT(DT_IRQ_BY_IDX(node_id, idx, irq), \ DT_IRQ_BY_IDX(node_id, idx, priority), \ CAN_NXP_S32_IRQ_HANDLER(n, \ DT_STRING_TOKEN_BY_IDX(node_id, prop, idx)), \ NULL, \ 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_FOREACH_PROP_ELEM_VARGS(CAN_NXP_S32_NODE(n), interrupt_names, \ _CAN_NXP_S32_IRQ_CONFIG, n); \ } #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_GET(CAN_NXP_S32_NODE(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_GET(CAN_NXP_S32_NODE(n)); \ can_nxp_s32_ctrl_callback(dev, eventType, buffIdx, canexcelState); \ } #if defined(CONFIG_CAN_FD_MODE) #define CAN_NXP_S32_TIMING_DATA_CONFIG(n) \ .bitrate_data = DT_PROP(CAN_NXP_S32_NODE(n), bus_speed_data), \ .sjw_data = DT_PROP(CAN_NXP_S32_NODE(n), sjw_data), \ .prop_seg_data = DT_PROP_OR(CAN_NXP_S32_NODE(n), prop_seg_data, 0), \ .phase_seg1_data = DT_PROP_OR(CAN_NXP_S32_NODE(n), phase_seg1_data, 0), \ .phase_seg2_data = DT_PROP_OR(CAN_NXP_S32_NODE(n), phase_seg2_data, 0), \ .sample_point_data = DT_PROP_OR(CAN_NXP_S32_NODE(n), sample_point_data, 0), #define CAN_NXP_S32_FD_MODE 1 #define CAN_NXP_S32_BRS 1 #else #define CAN_NXP_S32_TIMING_DATA_CONFIG(n) #define CAN_NXP_S32_FD_MODE 0 #define CAN_NXP_S32_BRS 0 #endif #ifdef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY #define CAN_NXP_S32_CTRL_OPTIONS CANXL_IP_BUSOFF_RECOVERY_U32 #else #define CAN_NXP_S32_CTRL_OPTIONS 0 #endif #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_DEFINE(CAN_NXP_S32_NODE(n)); \ Canexcel_Ip_ConfigType can_nxp_s32_default_config##n = { \ .rx_mbdesc = (uint8)CONFIG_CAN_NXP_S32_MAX_RX, \ .tx_mbdesc = (uint8)CONFIG_CAN_NXP_S32_MAX_TX, \ .CanxlMode = CANEXCEL_LISTEN_ONLY_MODE, \ .fd_enable = (boolean)CAN_NXP_S32_FD_MODE, \ .bitRateSwitch = (boolean)CAN_NXP_S32_BRS, \ .ctrlOptions = (uint32)CAN_NXP_S32_CTRL_OPTIONS, \ .Callback = nxp_s32_can_##n##_ctrl_callback, \ .ErrorCallback = nxp_s32_can_##n##_err_callback \ }; \ __nocache Canexcel_Ip_StateType can_nxp_s32_state##n; \ __nocache Canexcel_TxFdMsgType tx_msg##n[CONFIG_CAN_NXP_S32_MAX_TX]; \ __nocache Canexcel_RxFdMsg rx_msg_##n[CONFIG_CAN_NXP_S32_MAX_RX]; \ 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, \ .rx_msg = rx_msg_##n, \ }; \ static struct can_nxp_s32_config can_nxp_s32_config_##n = { \ .base_sic = (CANXL_SIC_Type *) \ DT_REG_ADDR_BY_NAME(CAN_NXP_S32_NODE(n), sic), \ .base_grp_ctrl = (CANXL_GRP_CONTROL_Type *) \ DT_REG_ADDR_BY_NAME(CAN_NXP_S32_NODE(n), grp_ctrl), \ .base_dsc_ctrl = (CANXL_DSC_CONTROL_Type *) \ DT_REG_ADDR_BY_NAME(CAN_NXP_S32_NODE(n), dsc_ctrl), \ .instance = n, \ .clock_dev = DEVICE_DT_GET(DT_CLOCKS_CTLR(CAN_NXP_S32_NODE(n))), \ .clock_subsys = (clock_control_subsys_t) \ DT_CLOCKS_CELL(CAN_NXP_S32_NODE(n), name), \ .bitrate = DT_PROP(CAN_NXP_S32_NODE(n), bus_speed), \ .sjw = DT_PROP(CAN_NXP_S32_NODE(n), sjw), \ .prop_seg = DT_PROP_OR(CAN_NXP_S32_NODE(n), prop_seg, 0), \ .phase_seg1 = DT_PROP_OR(CAN_NXP_S32_NODE(n), phase_seg1, 0), \ .phase_seg2 = DT_PROP_OR(CAN_NXP_S32_NODE(n), phase_seg2, 0), \ .sample_point = DT_PROP_OR(CAN_NXP_S32_NODE(n), sample_point, 0), \ CAN_NXP_S32_TIMING_DATA_CONFIG(n) \ .max_bitrate = DT_CAN_TRANSCEIVER_MAX_BITRATE(CAN_NXP_S32_NODE(n), \ CAN_NXP_S32_MAX_BITRATE), \ .phy = DEVICE_DT_GET_OR_NULL(DT_PHANDLE(CAN_NXP_S32_NODE(n), phys)), \ .pin_cfg = PINCTRL_DT_DEV_CONFIG_GET(CAN_NXP_S32_NODE(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_DEFINE(CAN_NXP_S32_NODE(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); #if DT_NODE_HAS_STATUS(CAN_NXP_S32_NODE(0), okay) CAN_NXP_S32_INIT_DEVICE(0) #endif #if DT_NODE_HAS_STATUS(CAN_NXP_S32_NODE(1), okay) CAN_NXP_S32_INIT_DEVICE(1) #endif