/* * Copyright (c) 2019 Alexander Wachter * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include LOG_MODULE_REGISTER(can_common, CONFIG_CAN_LOG_LEVEL); /* Maximum acceptable deviation in sample point location (permille) */ #define SAMPLE_POINT_MARGIN 50 /* CAN sync segment is always one time quantum */ #define CAN_SYNC_SEG 1 struct can_tx_default_cb_ctx { struct k_sem done; int status; }; static void can_tx_default_cb(const struct device *dev, int error, void *user_data) { struct can_tx_default_cb_ctx *ctx = user_data; ctx->status = error; k_sem_give(&ctx->done); } int z_impl_can_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_driver_api *api = (const struct can_driver_api *)dev->api; if (callback == NULL) { struct can_tx_default_cb_ctx ctx; int err; k_sem_init(&ctx.done, 0, 1); err = api->send(dev, frame, timeout, can_tx_default_cb, &ctx); if (err != 0) { return err; } k_sem_take(&ctx.done, K_FOREVER); return ctx.status; } return api->send(dev, frame, timeout, callback, user_data); } static void can_msgq_put(const struct device *dev, struct can_frame *frame, void *user_data) { struct k_msgq *msgq = (struct k_msgq *)user_data; int ret; ARG_UNUSED(dev); __ASSERT_NO_MSG(msgq); ret = k_msgq_put(msgq, frame, K_NO_WAIT); if (ret) { LOG_ERR("Msgq %p overflowed. Frame ID: 0x%x", msgq, frame->id); } } int z_impl_can_add_rx_filter_msgq(const struct device *dev, struct k_msgq *msgq, const struct can_filter *filter) { const struct can_driver_api *api = dev->api; return api->add_rx_filter(dev, can_msgq_put, msgq, filter); } static int update_sampling_pnt(uint32_t ts, uint32_t sp, struct can_timing *res, const struct can_timing *max, const struct can_timing *min) { uint16_t ts1_max = max->phase_seg1 + max->prop_seg; uint16_t ts1_min = min->phase_seg1 + min->prop_seg; uint32_t sp_calc; uint16_t ts1, ts2; ts2 = ts - (ts * sp) / 1000; ts2 = CLAMP(ts2, min->phase_seg2, max->phase_seg2); ts1 = ts - CAN_SYNC_SEG - ts2; if (ts1 > ts1_max) { ts1 = ts1_max; ts2 = ts - CAN_SYNC_SEG - ts1; if (ts2 > max->phase_seg2) { return -1; } } else if (ts1 < ts1_min) { ts1 = ts1_min; ts2 = ts - ts1; if (ts2 < min->phase_seg2) { return -1; } } res->prop_seg = CLAMP(ts1 / 2, min->prop_seg, max->prop_seg); res->phase_seg1 = ts1 - res->prop_seg; res->phase_seg2 = ts2; sp_calc = (CAN_SYNC_SEG + ts1) * 1000 / ts; return sp_calc > sp ? sp_calc - sp : sp - sp_calc; } /* Internal function to do the actual calculation */ static int can_calc_timing_int(uint32_t core_clock, struct can_timing *res, const struct can_timing *min, const struct can_timing *max, uint32_t bitrate, uint16_t sp) { uint32_t ts = max->prop_seg + max->phase_seg1 + max->phase_seg2 + CAN_SYNC_SEG; uint16_t sp_err_min = UINT16_MAX; int sp_err; struct can_timing tmp_res; if (bitrate == 0 || sp >= 1000) { return -EINVAL; } for (int prescaler = MAX(core_clock / (ts * bitrate), 1); prescaler <= max->prescaler; ++prescaler) { if (core_clock % (prescaler * bitrate)) { /* No integer ts */ continue; } ts = core_clock / (prescaler * bitrate); sp_err = update_sampling_pnt(ts, sp, &tmp_res, max, min); if (sp_err < 0) { /* No prop_seg, seg1, seg2 combination possible */ continue; } if (sp_err < sp_err_min) { sp_err_min = sp_err; res->prop_seg = tmp_res.prop_seg; res->phase_seg1 = tmp_res.phase_seg1; res->phase_seg2 = tmp_res.phase_seg2; res->prescaler = (uint16_t)prescaler; if (sp_err == 0) { /* No better result than a perfect match*/ break; } } } if (sp_err_min) { LOG_DBG("SP error: %d 1/1000", sp_err_min); } return sp_err_min == UINT16_MAX ? -ENOTSUP : (int)sp_err_min; } int z_impl_can_calc_timing(const struct device *dev, struct can_timing *res, uint32_t bitrate, uint16_t sample_pnt) { const struct can_timing *min = can_get_timing_min(dev); const struct can_timing *max = can_get_timing_max(dev); uint32_t core_clock; int ret; if (bitrate > 1000000) { return -EINVAL; } ret = can_get_core_clock(dev, &core_clock); if (ret != 0) { return ret; } return can_calc_timing_int(core_clock, res, min, max, bitrate, sample_pnt); } #ifdef CONFIG_CAN_FD_MODE int z_impl_can_calc_timing_data(const struct device *dev, struct can_timing *res, uint32_t bitrate, uint16_t sample_pnt) { const struct can_timing *min = can_get_timing_data_min(dev); const struct can_timing *max = can_get_timing_data_max(dev); uint32_t core_clock; int ret; if (bitrate > 8000000) { return -EINVAL; } ret = can_get_core_clock(dev, &core_clock); if (ret != 0) { return ret; } return can_calc_timing_int(core_clock, res, min, max, bitrate, sample_pnt); } #endif /* CONFIG_CAN_FD_MODE */ int can_calc_prescaler(const struct device *dev, struct can_timing *timing, uint32_t bitrate) { uint32_t ts = timing->prop_seg + timing->phase_seg1 + timing->phase_seg2 + CAN_SYNC_SEG; uint32_t core_clock; int ret; ret = can_get_core_clock(dev, &core_clock); if (ret != 0) { return ret; } timing->prescaler = core_clock / (bitrate * ts); return core_clock % (ts * timing->prescaler); } /** * @brief Get the sample point location for a given bitrate * * @param bitrate The bitrate in bits/second. * @return The sample point in permille. */ uint16_t sample_point_for_bitrate(uint32_t bitrate) { uint16_t sample_pnt; if (bitrate > 800000) { /* 75.0% */ sample_pnt = 750; } else if (bitrate > 500000) { /* 80.0% */ sample_pnt = 800; } else { /* 87.5% */ sample_pnt = 875; } return sample_pnt; } int z_impl_can_set_bitrate(const struct device *dev, uint32_t bitrate) { struct can_timing timing; uint32_t max_bitrate; uint16_t sample_pnt; int ret; ret = can_get_max_bitrate(dev, &max_bitrate); if (ret == -ENOSYS) { /* Maximum bitrate unknown */ max_bitrate = 0; } else if (ret < 0) { return ret; } if ((max_bitrate > 0) && (bitrate > max_bitrate)) { return -ENOTSUP; } sample_pnt = sample_point_for_bitrate(bitrate); ret = can_calc_timing(dev, &timing, bitrate, sample_pnt); if (ret < 0) { return ret; } if (ret > SAMPLE_POINT_MARGIN) { return -ERANGE; } timing.sjw = CAN_SJW_NO_CHANGE; return can_set_timing(dev, &timing); } #ifdef CONFIG_CAN_FD_MODE int z_impl_can_set_bitrate_data(const struct device *dev, uint32_t bitrate_data) { struct can_timing timing_data; uint32_t max_bitrate; uint16_t sample_pnt; int ret; ret = can_get_max_bitrate(dev, &max_bitrate); if (ret == -ENOSYS) { /* Maximum bitrate unknown */ max_bitrate = 0; } else if (ret < 0) { return ret; } if ((max_bitrate > 0) && (bitrate_data > max_bitrate)) { return -ENOTSUP; } sample_pnt = sample_point_for_bitrate(bitrate_data); ret = can_calc_timing_data(dev, &timing_data, bitrate_data, sample_pnt); if (ret < 0) { return ret; } if (ret > SAMPLE_POINT_MARGIN) { return -ERANGE; } timing_data.sjw = CAN_SJW_NO_CHANGE; return can_set_timing_data(dev, &timing_data); } #endif /* CONFIG_CAN_FD_MODE */