diff --git a/drivers/can/Kconfig b/drivers/can/Kconfig index 69d40532f97c49..aae8c55aadd032 100644 --- a/drivers/can/Kconfig +++ b/drivers/can/Kconfig @@ -24,6 +24,11 @@ config CAN_SHELL help Enable CAN Shell for testing. +config CAN_FD_MODE + bool + help + Enable CAN-FD compatible API + config CAN_INIT_PRIORITY int "CAN driver init priority" default 80 diff --git a/drivers/can/can_common.c b/drivers/can/can_common.c index c1bbc59b84092a..45d7af03e3392f 100644 --- a/drivers/can/can_common.c +++ b/drivers/can/can_common.c @@ -11,6 +11,9 @@ #include LOG_MODULE_REGISTER(can_driver); +#define CAN_CLAMP(val, min, max) MIN(MAX(val, min), max) +#define CAN_SYNC_SEG 1 + #define WORK_BUF_COUNT_IS_POWER_OF_2 !(CONFIG_CAN_WORKQ_FRAMES_BUF_CNT & \ (CONFIG_CAN_WORKQ_FRAMES_BUF_CNT - 1)) @@ -143,3 +146,144 @@ int can_attach_workq(const struct device *dev, struct k_work_q *work_q, return api->attach_isr(dev, can_work_isr_put, work, 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 = CAN_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 (sp >= 1000 || + (!IS_ENABLED(CONFIG_CAN_FD_MODE) && bitrate > 1000000) || + (IS_ENABLED(CONFIG_CAN_FD_MODE) && bitrate > 8000000)) { + 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 = tmp_res; + 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 ? -EINVAL : (int)sp_err_min; +} + +int can_calc_timing(const struct device *dev, struct can_timing *res, + uint32_t bitrate, uint16_t sample_pnt) +{ + const struct can_driver_api *api = dev->api; + uint32_t core_clock; + int ret; + + ret = can_get_core_clock(dev, &core_clock); + if (ret != 0) { + return ret; + } + + return can_calc_timing_int(core_clock, res, &api->timing_min, + &api->timing_max, bitrate, sample_pnt); +} + +#ifdef CONFIG_CAN_FD_MODE +int can_calc_timing_data(const struct device *dev, struct can_timing *res, + uint32_t bitrate, uint16_t sample_pnt) +{ + const struct can_driver_api *api = dev->api; + uint32_t core_clock; + int ret; + + ret = can_get_core_clock(dev, &core_clock); + if (ret != 0) { + return ret; + } + + return can_calc_timing_int(core_clock, res, &api->timing_min_data, + &api->timing_max_data, bitrate, sample_pnt); +} +#endif + +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); +} diff --git a/drivers/can/can_handlers.c b/drivers/can/can_handlers.c index bc91fd3dfe1402..329bcab2a465f0 100644 --- a/drivers/can/can_handlers.c +++ b/drivers/can/can_handlers.c @@ -7,18 +7,39 @@ #include #include -static inline int z_vrfy_can_configure(const struct device *dev, - enum can_mode mode, - uint32_t bitrate) +static inline int z_vrfy_can_set_timing(const struct device *dev, + const struct can_timing *timing, + const struct can_timing *timing_data); { + Z_OOPS(Z_SYSCALL_DRIVER_CAN(dev, set_timing)); - Z_OOPS(Z_SYSCALL_DRIVER_CAN(dev, configure)); + return z_impl_can_set_timing((const struct device *)dev, + (const struct can_timing *)timing, + (const struct can_timing *)timing_data); +} +#include + +static inline int z_vrfy_can_set_bitrate(const struct device *dev, + uint32_t bitrate) +{ + + Z_OOPS(Z_SYSCALL_DRIVER_CAN(dev, set_bitrate)); + + return z_impl_can_set_bitrate((const struct device *)dev, + (uint32_t)bitrate); +} +#include + +static inline int z_vrfy_can_get_core_clock(const struct device *dev, + uint32_t *rate) +{ + + Z_OOPS(Z_SYSCALL_DRIVER_CAN(dev, get_core_clock)); + Z_OOPS(Z_SYSCALL_MEMORY_WRITE(rate, sizeof(rate))); - return z_impl_can_configure((const struct device *)dev, - (enum can_mode)mode, - (uint32_t)bitrate); + return z_impl_can_get_core_clock(dev, rate); } -#include +#include static inline int z_vrfy_can_send(const struct device *dev, const struct zcan_frame *msg, diff --git a/drivers/can/can_loopback.c b/drivers/can/can_loopback.c index ac36af86e07b02..cb0b4ba7988f20 100644 --- a/drivers/can/can_loopback.c +++ b/drivers/can/can_loopback.c @@ -193,17 +193,24 @@ void can_loopback_detach(const struct device *dev, int filter_id) k_mutex_unlock(&data->mtx); } -int can_loopback_configure(const struct device *dev, enum can_mode mode, - uint32_t bitrate) +int can_loopback_set_mode(const struct device *dev, enum can_mode mode) { struct can_loopback_data *data = DEV_DATA(dev); - ARG_UNUSED(bitrate); - data->loopback = mode == CAN_LOOPBACK_MODE ? 1 : 0; return 0; } +int can_loopback_set_timing(const struct device *dev, + const struct can_timing *timing, + const struct can_timing *timing_data) +{ + ARG_UNUSED(dev); + ARG_UNUSED(timing); + ARG_UNUSED(timing_data); + return 0; +} + static enum can_state can_loopback_get_state(const struct device *dev, struct can_bus_err_cnt *err_cnt) { @@ -234,8 +241,16 @@ static void can_loopback_register_state_change_isr(const struct device *dev, ARG_UNUSED(isr); } +int can_loopback_get_core_clock(const struct device *dev, uint32_t *rate) +{ + /* Return 16MHz as an realistic value for the testcases */ + *rate = 16000000; + return 0; +} + static const struct can_driver_api can_api_funcs = { - .configure = can_loopback_configure, + .set_mode = can_loopback_set_mode, + .set_timing = can_loopback_set_timing, .send = can_loopback_send, .attach_isr = can_loopback_attach_isr, .detach = can_loopback_detach, @@ -243,10 +258,24 @@ static const struct can_driver_api can_api_funcs = { #ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY .recover = can_loopback_recover, #endif - .register_state_change_isr = can_loopback_register_state_change_isr + .register_state_change_isr = can_loopback_register_state_change_isr, + .get_core_clock = can_loopback_get_core_clock, + .timing_min = { + .sjw = 0x1, + .prop_seg = 0x00, + .phase_seg1 = 0x01, + .phase_seg2 = 0x01, + .prescaler = 0x01 + }, + .timing_max = { + .sjw = 0x0F, + .prop_seg = 0x0F, + .phase_seg1 = 0x0F, + .phase_seg2 = 0x0F, + .prescaler = 0xFFFF + } }; - static int can_loopback_init(const struct device *dev) { struct can_loopback_data *data = DEV_DATA(dev); diff --git a/drivers/can/can_mcp2515.c b/drivers/can/can_mcp2515.c index acbccfa117ed74..ced73be7de6c8e 100644 --- a/drivers/can/can_mcp2515.c +++ b/drivers/can/can_mcp2515.c @@ -254,7 +254,7 @@ static void mcp2515_convert_mcp2515frame_to_zcanframe(const uint8_t *source, } } -const int mcp2515_set_mode(const struct device *dev, uint8_t mcp2515_mode) +const int mcp2515_set_mode_int(const struct device *dev, uint8_t mcp2515_mode) { uint8_t canstat; @@ -290,34 +290,41 @@ static int mcp2515_get_mode(const struct device *dev, uint8_t *mode) return 0; } -static int mcp2515_configure(const struct device *dev, enum can_mode mode, - uint32_t bitrate) +static int mcp2515_get_core_clock(const struct device *dev, uint32_t *rate) { const struct mcp2515_config *dev_cfg = DEV_CFG(dev); + + *rate = dev_cfg->osc_freq / 2; + return 0; +} + + +static int mcp2515_set_timing(const struct device *dev, + const struct can_timing *timing, + const struct can_timing *timing_data) +{ + ARG_UNUSED(timing_data); struct mcp2515_data *dev_data = DEV_DATA(dev); int ret; + if (!timing) { + return -EINVAL; + } + /* CNF3, CNF2, CNF1, CANINTE */ uint8_t config_buf[4]; uint8_t reset_mode; - if (bitrate == 0) { - bitrate = dev_cfg->bus_speed; - } - - const uint8_t bit_length = 1 + dev_cfg->tq_prop + dev_cfg->tq_bs1 + - dev_cfg->tq_bs2; - /* CNF1; SJW<7:6> | BRP<5:0> */ - uint8_t brp = (dev_cfg->osc_freq / (bit_length * bitrate * 2)) - 1; - const uint8_t sjw = (dev_cfg->tq_sjw - 1) << 6; + uint8_t brp = timing->prescaler; + const uint8_t sjw = (timing->sjw - 1) << 6; uint8_t cnf1 = sjw | brp; /* CNF2; BTLMODE<7>|SAM<6>|PHSEG1<5:3>|PRSEG<2:0> */ const uint8_t btlmode = 1 << 7; const uint8_t sam = 0 << 6; - const uint8_t phseg1 = (dev_cfg->tq_bs1 - 1) << 3; - const uint8_t prseg = (dev_cfg->tq_prop - 1); + const uint8_t phseg1 = (timing->phase_seg1 - 1) << 3; + const uint8_t prseg = (timing->prop_seg - 1); const uint8_t cnf2 = btlmode | sam | phseg1 | prseg; @@ -325,7 +332,7 @@ static int mcp2515_configure(const struct device *dev, enum can_mode mode, const uint8_t sof = 0 << 7; const uint8_t wakfil = 0 << 6; const uint8_t und = 0 << 3; - const uint8_t phseg2 = (dev_cfg->tq_bs2 - 1); + const uint8_t phseg2 = (timing->phase_seg2 - 1); const uint8_t cnf3 = sof | wakfil | und | phseg2; @@ -338,26 +345,17 @@ static int mcp2515_configure(const struct device *dev, enum can_mode mode, const uint8_t rx0_ctrl = BIT(6) | BIT(5) | BIT(2); const uint8_t rx1_ctrl = BIT(6) | BIT(5); - __ASSERT((dev_cfg->tq_sjw >= 1) && (dev_cfg->tq_sjw <= 4), + __ASSERT((timing->sjw >= 1) && (timing->sjw <= 4), "1 <= SJW <= 4"); - __ASSERT((dev_cfg->tq_prop >= 1) && (dev_cfg->tq_prop <= 8), + __ASSERT((timing->prop_seg >= 1) && (timing->prop_seg <= 8), "1 <= PROP <= 8"); - __ASSERT((dev_cfg->tq_bs1 >= 1) && (dev_cfg->tq_bs1 <= 8), + __ASSERT((timing->phase_seg1 >= 1) && (timing->phase_seg1 <= 8), "1 <= BS1 <= 8"); - __ASSERT((dev_cfg->tq_bs2 >= 2) && (dev_cfg->tq_bs2 <= 8), + __ASSERT((timing->phase_seg2 >= 2) && (timing->phase_seg2 <= 8), "2 <= BS2 <= 8"); - __ASSERT(dev_cfg->tq_prop + dev_cfg->tq_bs1 >= dev_cfg->tq_bs2, + __ASSERT(timing->prop_seg + timing->phase_seg1 >= timing->phase_seg2, "PROP + BS1 >= BS2"); - __ASSERT(dev_cfg->tq_bs2 > dev_cfg->tq_sjw, "BS2 > SJW"); - - if (dev_cfg->osc_freq % (bit_length * bitrate * 2)) { - LOG_ERR("Prescaler is not a natural number! " - "prescaler = osc_rate / ((PROP + SEG1 + SEG2 + 1) " - "* bitrate * 2)\n" - "prescaler = %d / ((%d + %d + %d + 1) * %d * 2)", - dev_cfg->osc_freq, dev_cfg->tq_prop, - dev_cfg->tq_bs1, dev_cfg->tq_bs2, bitrate); - } + __ASSERT(timing->phase_seg2 > timing->sjw, "BS2 > SJW"); config_buf[0] = cnf3; config_buf[1] = cnf2; @@ -366,10 +364,7 @@ static int mcp2515_configure(const struct device *dev, enum can_mode mode, k_mutex_lock(&dev_data->mutex, K_FOREVER); - /* Wait 128 OSC1 clock cycles at 1MHz (minimum clock in frequency) - * see MCP2515 datasheet section 8.1 Oscillator Start-up Timer - */ - k_usleep(128); + k_usleep(MCP2515_OSC_STARTUP_US); /* will enter configuration mode automatically */ ret = mcp2515_cmd_soft_reset(dev); @@ -378,7 +373,7 @@ static int mcp2515_configure(const struct device *dev, enum can_mode mode, goto done; } - k_usleep(128); + k_usleep(MCP2515_OSC_STARTUP_US); ret = mcp2515_get_mode(dev, &reset_mode); if (ret < 0) { @@ -413,8 +408,20 @@ static int mcp2515_configure(const struct device *dev, enum can_mode mode, } done: - ret = mcp2515_set_mode(dev, - mcp2515_convert_canmode_to_mcp2515mode(mode)); + k_mutex_unlock(&dev_data->mutex); + return ret; +} + +static int mcp2515_set_mode(const struct device *dev, enum can_mode mode) +{ + struct mcp2515_data *dev_data = DEV_DATA(dev); + int ret; + + k_mutex_lock(&dev_data->mutex, K_FOREVER); + k_usleep(MCP2515_OSC_STARTUP_US); + + ret = mcp2515_set_mode_int(dev, + mcp2515_convert_canmode_to_mcp2515mode(mode)); if (ret < 0) { LOG_ERR("Failed to set the mode [%d]", ret); } @@ -772,7 +779,8 @@ static void mcp2515_int_gpio_callback(const struct device *dev, } static const struct can_driver_api can_api_funcs = { - .configure = mcp2515_configure, + .set_timing = mcp2515_set_timing, + .set_mode = mcp2515_set_mode, .send = mcp2515_send, .attach_isr = mcp2515_attach_isr, .detach = mcp2515_detach, @@ -780,7 +788,22 @@ static const struct can_driver_api can_api_funcs = { #ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY .recover = mcp2515_recover, #endif - .register_state_change_isr = mcp2515_register_state_change_isr + .register_state_change_isr = mcp2515_register_state_change_isr, + .get_core_clock = mcp2515_get_core_clock, + .timing_min = { + .sjw = 0x1, + .prop_seg = 0x01, + .phase_seg1 = 0x01, + .phase_seg2 = 0x01, + .prescaler = 0x01 + }, + .timing_max = { + .sjw = 0x04, + .prop_seg = 0x08, + .phase_seg1 = 0x08, + .phase_seg2 = 0x08, + .prescaler = 0x20 + } }; @@ -789,6 +812,7 @@ static int mcp2515_init(const struct device *dev) const struct mcp2515_config *dev_cfg = DEV_CFG(dev); struct mcp2515_data *dev_data = DEV_DATA(dev); int ret; + struct can_timing timing; k_sem_init(&dev_data->int_sem, 0, 1); k_mutex_init(&dev_data->mutex); @@ -867,7 +891,33 @@ static int mcp2515_init(const struct device *dev) (void)memset(dev_data->filter, 0, sizeof(dev_data->filter)); dev_data->old_state = CAN_ERROR_ACTIVE; - ret = mcp2515_configure(dev, CAN_NORMAL_MODE, dev_cfg->bus_speed); + timing.sjw = dev_cfg->tq_sjw; + if (dev_cfg->sample_point) { + ret = can_calc_timing(dev, &timing, dev_cfg->bus_speed, + dev_cfg->sample_point); + if (ret == -EINVAL) { + LOG_ERR("Can't find timing for given param"); + return -EIO; + } + LOG_DBG("Presc: %d, BS1: %d, BS2: %d", + timing.prescaler, timing.phase_seg1, timing.phase_seg2); + LOG_DBG("Sample-point err : %d", ret); + } else { + timing.prop_seg = dev_cfg->tq_prop; + timing.phase_seg1 = dev_cfg->tq_bs1; + timing.phase_seg2 = dev_cfg->tq_bs2; + ret = can_calc_prescaler(dev, &timing, dev_cfg->bus_speed); + if (ret) { + LOG_WRN("Bitrate error: %d", ret); + } + } + + ret = can_set_mode(dev, CAN_NORMAL_MODE); + if (ret) { + return ret; + } + + ret = can_set_timing(dev, &timing, NULL); return ret; } @@ -900,11 +950,18 @@ static const struct mcp2515_config mcp2515_config_1 = { .spi_cs_flags = DT_INST_SPI_DEV_CS_GPIOS_FLAGS(0), #endif /* DT_INST_SPI_DEV_HAS_CS_GPIOS(0) */ .tq_sjw = DT_INST_PROP(0, sjw), +#if DT_INST_PROP(0, prop_seg) > 0 && \ + DT_INST_PROP(0, phase_seg1) > 0 && \ + DT_INST_PROP(0, phase_seg2) > 0 .tq_prop = DT_INST_PROP(0, prop_seg), .tq_bs1 = DT_INST_PROP(0, phase_seg1), .tq_bs2 = DT_INST_PROP(0, phase_seg2), +#endif .bus_speed = DT_INST_PROP(0, bus_speed), .osc_freq = DT_INST_PROP(0, osc_freq) +#if DT_INST_PROP(0, sample_point) + .sample_point = DT_INST_PROP(0, sample_point), +#endif }; DEVICE_AND_API_INIT(can_mcp2515_1, DT_INST_LABEL(0), &mcp2515_init, diff --git a/drivers/can/can_mcp2515.h b/drivers/can/can_mcp2515.h index 4313d3b04f798f..5e6c850bbd2c38 100644 --- a/drivers/can/can_mcp2515.h +++ b/drivers/can/can_mcp2515.h @@ -78,8 +78,15 @@ struct mcp2515_config { uint8_t tq_bs2; uint32_t bus_speed; uint32_t osc_freq; + uint16_t sample_point; }; +/* + * Startup time of 128 OSC1 clock cycles at 1MHz (minimum clock in frequency) + * see MCP2515 datasheet section 8.1 Oscillator Start-up Timer + */ +#define MCP2515_OSC_STARTUP_US 128U + /* MCP2515 Opcodes */ #define MCP2515_OPCODE_WRITE 0x02 #define MCP2515_OPCODE_READ 0x03 diff --git a/drivers/can/can_mcux_flexcan.c b/drivers/can/can_mcux_flexcan.c index 0c9b0b05df42db..adea01704e2c11 100644 --- a/drivers/can/can_mcux_flexcan.c +++ b/drivers/can/can_mcux_flexcan.c @@ -56,6 +56,7 @@ struct mcux_flexcan_config { clock_control_subsys_t clock_subsys; int clk_source; uint32_t bitrate; + uint32_t sample_point; uint32_t sjw; uint32_t prop_seg; uint32_t phase_seg1; @@ -91,36 +92,69 @@ struct mcux_flexcan_data { struct mcux_flexcan_tx_callback tx_cbs[MCUX_FLEXCAN_MAX_TX]; enum can_state state; can_state_change_isr_t state_change_isr; + flexcan_timing_config_t timing; }; -static int mcux_flexcan_configure(const struct device *dev, - enum can_mode mode, - uint32_t bitrate) +static int mcux_flexcan_get_core_clock(const struct device *dev, uint32_t *rate) { const struct mcux_flexcan_config *config = dev->config; - flexcan_config_t flexcan_config; const struct device *clock_dev; - uint32_t clock_freq; clock_dev = device_get_binding(config->clock_name); if (clock_dev == NULL) { - return -EINVAL; + return -EIO; } - if (clock_control_get_rate(clock_dev, config->clock_subsys, - &clock_freq)) { + return clock_control_get_rate(clock_dev, config->clock_subsys, rate); +} + +static int mcux_flexcan_set_timing(const struct device *dev, + const struct can_timing *timing, + const struct can_timing *timing_data) +{ + ARG_UNUSED(timing_data); + struct mcux_flexcan_data *data = dev->data; + const struct mcux_flexcan_config *config = dev->config; + + if (!timing) { return -EINVAL; } + data->timing.preDivider = timing->prescaler; + data->timing.rJumpwidth = timing->sjw; + data->timing.phaseSeg1 = timing->phase_seg1; + data->timing.phaseSeg2 = timing->phase_seg2; + data->timing.propSeg = timing->prop_seg; + + FLEXCAN_SetTimingConfig(config->base, &data->timing); + + return 0; +} + +static int mcux_flexcan_set_mode(const struct device *dev, enum can_mode mode) +{ + struct mcux_flexcan_data *data = dev->data; + const struct mcux_flexcan_config *config = dev->config; + flexcan_config_t flexcan_config; + uint32_t clock_freq; + int ret; + + ret = mcux_flexcan_get_core_clock(dev, &clock_freq); + if (ret != 0) { + return -EIO; + } + FLEXCAN_GetDefaultConfig(&flexcan_config); flexcan_config.clkSrc = config->clk_source; - flexcan_config.baudRate = bitrate ? bitrate : config->bitrate; + flexcan_config.baudRate = clock_freq / + (1U + data->timing.propSeg + data->timing.phaseSeg1 + + data->timing.phaseSeg2) / data->timing.preDivider; flexcan_config.enableIndividMask = true; - flexcan_config.timingConfig.rJumpwidth = config->sjw; - flexcan_config.timingConfig.propSeg = config->prop_seg; - flexcan_config.timingConfig.phaseSeg1 = config->phase_seg1; - flexcan_config.timingConfig.phaseSeg2 = config->phase_seg2; + flexcan_config.timingConfig.rJumpwidth = data->timing.rJumpwidth; + flexcan_config.timingConfig.propSeg = data->timing.propSeg; + flexcan_config.timingConfig.phaseSeg1 = data->timing.phaseSeg1; + flexcan_config.timingConfig.phaseSeg2 = data->timing.phaseSeg2; if (mode == CAN_LOOPBACK_MODE || mode == CAN_SILENT_LOOPBACK_MODE) { flexcan_config.enableLoopBack = true; @@ -623,6 +657,7 @@ static int mcux_flexcan_init(const struct device *dev) { const struct mcux_flexcan_config *config = dev->config; struct mcux_flexcan_data *data = dev->data; + struct can_timing timing; int err; int i; @@ -633,7 +668,37 @@ static int mcux_flexcan_init(const struct device *dev) k_sem_init(&data->tx_cbs[i].done, 0, 1); } - err = mcux_flexcan_configure(dev, CAN_NORMAL_MODE, 0); + timing.sjw = config->sjw; + if (config->sample_point) { + err = can_calc_timing(dev, &timing, config->bitrate, + config->sample_point); + if (err == -EINVAL) { + LOG_ERR("Can't find timing for given param"); + return -EIO; + } + LOG_DBG("Presc: %d, Seg1S1: %d, Seg2: %d", + timing.prescaler, timing.phase_seg1, timing.phase_seg2); + LOG_DBG("Sample-point err : %d", err); + } else if (config->phase_seg1) { + timing.prop_seg = config->prop_seg; + timing.phase_seg1 = config->phase_seg1; + timing.phase_seg2 = config->phase_seg2; + err = can_calc_prescaler(dev, &timing, config->bitrate); + if (err) { + LOG_WRN("Bitrate error: %d", err); + } + } else { + LOG_ERR("Timing not configured"); + return -EINVAL; + } + + data->timing.preDivider = timing.prescaler; + data->timing.rJumpwidth = timing.sjw; + data->timing.phaseSeg1 = timing.phase_seg1; + data->timing.phaseSeg2 = timing.phase_seg2; + data->timing.propSeg = timing.prop_seg; + + err = mcux_flexcan_set_mode(dev, CAN_NORMAL_MODE); if (err) { return err; } @@ -654,7 +719,8 @@ static int mcux_flexcan_init(const struct device *dev) } static const struct can_driver_api mcux_flexcan_driver_api = { - .configure = mcux_flexcan_configure, + .set_mode = mcux_flexcan_set_mode, + .set_timing = mcux_flexcan_set_timing, .send = mcux_flexcan_send, .attach_isr = mcux_flexcan_attach_isr, .detach = mcux_flexcan_detach, @@ -662,7 +728,22 @@ static const struct can_driver_api mcux_flexcan_driver_api = { #ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY .recover = mcux_flexcan_recover, #endif - .register_state_change_isr = mcux_flexcan_register_state_change_isr + .register_state_change_isr = mcux_flexcan_register_state_change_isr, + .get_core_clock = mcux_flexcan_get_core_clock, + .timing_min = { + .sjw = 0x1, + .prop_seg = 0x01, + .phase_seg1 = 0x01, + .phase_seg2 = 0x01, + .prescaler = 0x01 + }, + .timing_max = { + .sjw = 0x03, + .prop_seg = 0x07, + .phase_seg1 = 0x07, + .phase_seg2 = 0x07, + .prescaler = 0xFF + } }; #define FLEXCAN_IRQ_CODE(id, name) \ diff --git a/drivers/can/can_shell.c b/drivers/can/can_shell.c index b414c8c6f641f2..05f8799b9bcbac 100644 --- a/drivers/can/can_shell.c +++ b/drivers/can/can_shell.c @@ -240,14 +240,21 @@ static int cmd_config(const struct shell *shell, size_t argc, char **argv) mode = CAN_NORMAL_MODE; } + ret = can_set_mode(can_dev, mode); + if (ret) { + shell_error(shell, "Failed to set mode [%d]", + ret); + return ret; + } + pos = read_bitrate(shell, pos, argv, &bitrate); if (pos < 0) { return -EINVAL; } - ret = can_configure(can_dev, mode, bitrate); + ret = can_set_bitrate(can_dev, bitrate, 0); if (ret) { - shell_error(shell, "Failed to configure CAN controller [%d]", + shell_error(shell, "Failed to set bitrate [%d]", ret); return ret; } diff --git a/drivers/can/can_stm32.c b/drivers/can/can_stm32.c index aa3162550f89f2..4b31d7253e462e 100644 --- a/drivers/can/can_stm32.c +++ b/drivers/can/can_stm32.c @@ -290,69 +290,60 @@ static int can_leave_sleep_mode(CAN_TypeDef *can) return 0; } -int can_stm32_runtime_configure(const struct device *dev, enum can_mode mode, - uint32_t bitrate) +int can_stm32_set_mode(const struct device *dev, enum can_mode mode) { - CAN_HandleTypeDef hcan; const struct can_stm32_config *cfg = DEV_CFG(dev); CAN_TypeDef *can = cfg->can; struct can_stm32_data *data = DEV_DATA(dev); - const struct device *clock; - uint32_t clock_rate; - uint32_t prescaler; - uint32_t reg_mode; - uint32_t ts1; - uint32_t ts2; - uint32_t sjw; int ret; - clock = device_get_binding(STM32_CLOCK_CONTROL_NAME); - __ASSERT_NO_MSG(clock); - hcan.Instance = can; - ret = clock_control_get_rate(clock, (clock_control_subsys_t *) &cfg->pclken, - &clock_rate); - if (ret != 0) { - LOG_ERR("Failed call clock_control_get_rate: return [%d]", ret); - return -EIO; - } + LOG_DBG("Set mode %d", mode); - if (!bitrate) { - bitrate = cfg->bus_speed; + k_mutex_lock(&data->inst_mutex, K_FOREVER); + ret = can_enter_init_mode(can); + if (ret) { + LOG_ERR("Failed to enter init mode"); + goto done; } - prescaler = clock_rate / (BIT_SEG_LENGTH(cfg) * bitrate); - if (prescaler == 0U || prescaler > 1024) { - LOG_ERR("HAL_CAN_Init failed: prescaler > max (%d > 1024)", - prescaler); - return -EINVAL; + switch (mode) { + case CAN_NORMAL_MODE: + can->BTR &= ~(CAN_BTR_LBKM | CAN_BTR_SILM); + break; + case CAN_LOOPBACK_MODE: + can->BTR &= ~(CAN_BTR_SILM); + can->BTR |= CAN_BTR_LBKM; + break; + case CAN_SILENT_MODE: + can->BTR &= ~(CAN_BTR_LBKM); + can->BTR |= CAN_BTR_SILM; + break; + case CAN_SILENT_LOOPBACK_MODE: + can->BTR |= CAN_BTR_LBKM | CAN_BTR_SILM; + break; + default: + break; } - if (clock_rate % (BIT_SEG_LENGTH(cfg) * bitrate)) { - LOG_ERR("Prescaler is not a natural number! " - "prescaler = clock_rate / ((PROP_SEG1 + SEG2 + 1)" - " * bus_speed); " - "prescaler = %d / ((%d + %d + 1) * %d)", - clock_rate, - cfg->prop_ts1, - cfg->ts2, - bitrate); +done: + ret = can_leave_init_mode(can); + if (ret) { + LOG_ERR("Failed to leave init mode"); } + k_mutex_unlock(&data->inst_mutex); + return ret; +} - __ASSERT(cfg->sjw >= 1, "SJW minimum is 1"); - __ASSERT(cfg->sjw <= 4, "SJW maximum is 4"); - __ASSERT(cfg->prop_ts1 >= 1, "PROP_TS1 minimum is 1"); - __ASSERT(cfg->prop_ts1 <= 16, "PROP_TS1 maximum is 16"); - __ASSERT(cfg->ts2 >= 1, "TS2 minimum is 1"); - __ASSERT(cfg->ts2 <= 8, "TS2 maximum is 8"); - - ts1 = ((cfg->prop_ts1 - 1) & 0x0F) << CAN_BTR_TS1_Pos; - ts2 = ((cfg->ts2 - 1) & 0x07) << CAN_BTR_TS2_Pos; - sjw = ((cfg->sjw - 1) & 0x07) << CAN_BTR_SJW_Pos; +int can_stm32_set_timing(const struct device *dev, + const struct can_timing *timing, + const struct can_timing *timing_data) +{ + const struct can_stm32_config *cfg = DEV_CFG(dev); + CAN_TypeDef *can = cfg->can; + struct can_stm32_data *data = DEV_DATA(dev); + int ret = -EIO; - reg_mode = (mode == CAN_NORMAL_MODE) ? 0U : - (mode == CAN_LOOPBACK_MODE) ? CAN_BTR_LBKM : - (mode == CAN_SILENT_MODE) ? CAN_BTR_SILM : - CAN_BTR_LBKM | CAN_BTR_SILM; + ARG_UNUSED(timing_data); k_mutex_lock(&data->inst_mutex, K_FOREVER); ret = can_enter_init_mode(can); @@ -361,26 +352,51 @@ int can_stm32_runtime_configure(const struct device *dev, enum can_mode mode, goto done; } - can->BTR = reg_mode | sjw | ts1 | ts2 | (prescaler - 1U); + can->BTR &= ~(CAN_BTR_BRP_Msk | CAN_BTR_TS1_Msk | + CAN_BTR_TS2_Msk | CAN_BTR_SJW_Msk); + + can->BTR |= (((timing->phase_seg1 - 1) & 0x0F) << CAN_BTR_TS1_Pos) | + (((timing->phase_seg2 - 1) & 0x07) << CAN_BTR_TS2_Pos) | + (((timing->sjw - 1) & 0x07) << CAN_BTR_SJW_Pos); ret = can_leave_init_mode(can); if (ret) { LOG_ERR("Failed to leave init mode"); - goto done; + } else { + ret = 0; } - LOG_DBG("Runtime configure of %s done", dev->name); - ret = 0; done: k_mutex_unlock(&data->inst_mutex); return ret; } +int can_stm32_get_core_clock(const struct device *dev, uint32_t *rate) +{ + const struct can_stm32_config *cfg = DEV_CFG(dev); + const struct device *clock; + int ret; + + clock = device_get_binding(STM32_CLOCK_CONTROL_NAME); + __ASSERT_NO_MSG(clock); + + ret = clock_control_get_rate(clock, + (clock_control_subsys_t *) &cfg->pclken, + rate); + if (ret != 0) { + LOG_ERR("Failed call clock_control_get_rate: return [%d]", ret); + return -EIO; + } + + return 0; +} + static int can_stm32_init(const struct device *dev) { const struct can_stm32_config *cfg = DEV_CFG(dev); struct can_stm32_data *data = DEV_DATA(dev); CAN_TypeDef *can = cfg->can; + struct can_timing timing; #if DT_NODE_HAS_STATUS(DT_NODELABEL(can2), okay) CAN_TypeDef *master_can = cfg->master_can; #endif @@ -445,8 +461,36 @@ static int can_stm32_init(const struct device *dev) #ifdef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY can->MCR |= CAN_MCR_ABOM; #endif + timing.sjw = cfg->sjw; + if (cfg->sample_point) { + ret = can_calc_timing(dev, &timing, cfg->bus_speed, + cfg->sample_point); + if (ret == -EINVAL) { + LOG_ERR("Can't find timing for given param"); + return -EIO; + } + LOG_DBG("Presc: %d, TS1: %d, TS2: %d", + timing.prescaler, timing.phase_seg1, timing.phase_seg2); + LOG_DBG("Sample-point err : %d", ret); + } else if (cfg->prop_ts1) { + timing.prop_seg = 0; + timing.phase_seg1 = cfg->prop_ts1; + timing.phase_seg2 = cfg->ts2; + ret = can_calc_prescaler(dev, &timing, cfg->bus_speed); + if (ret) { + LOG_WRN("Bitrate error: %d", ret); + } + } else { + LOG_ERR("Timing not configured"); + return -EINVAL; + } + + ret = can_stm32_set_timing(dev, &timing, NULL); + if (ret) { + return ret; + } - ret = can_stm32_runtime_configure(dev, CAN_NORMAL_MODE, 0); + ret = can_stm32_set_mode(dev, CAN_NORMAL_MODE); if (ret) { return ret; } @@ -1037,7 +1081,8 @@ void can_stm32_detach(const struct device *dev, int filter_nr) } static const struct can_driver_api can_api_funcs = { - .configure = can_stm32_runtime_configure, + .set_mode = can_stm32_set_mode, + .set_timing = can_stm32_set_timing, .send = can_stm32_send, .attach_isr = can_stm32_attach_isr, .detach = can_stm32_detach, @@ -1045,7 +1090,22 @@ static const struct can_driver_api can_api_funcs = { #ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY .recover = can_stm32_recover, #endif - .register_state_change_isr = can_stm32_register_state_change_isr + .register_state_change_isr = can_stm32_register_state_change_isr, + .get_core_clock = can_stm32_get_core_clock, + .timing_min = { + .sjw = 0x1, + .prop_seg = 0x00, + .phase_seg1 = 0x01, + .phase_seg2 = 0x01, + .prescaler = 0x01 + }, + .timing_max = { + .sjw = 0x07, + .prop_seg = 0x00, + .phase_seg1 = 0x0F, + .phase_seg2 = 0x07, + .prescaler = 0x400 + } }; #if DT_NODE_HAS_COMPAT_STATUS(DT_NODELABEL(can1), st_stm32_can, okay) @@ -1059,10 +1119,16 @@ static const struct can_stm32_config can_stm32_cfg_1 = { .can = (CAN_TypeDef *)DT_REG_ADDR(DT_NODELABEL(can1)), .master_can = (CAN_TypeDef *)DT_REG_ADDR(DT_NODELABEL(can1)), .bus_speed = DT_PROP(DT_NODELABEL(can1), bus_speed), +#if DT_PROP(DT_NODELABEL(can1), sample_point) > 0 + .sample_point = DT_PROP(DT_NODELABEL(can1), sample_point), +#endif .sjw = DT_PROP(DT_NODELABEL(can1), sjw), +#if DT_PROP(DT_NODELABEL(can1), phase_seg1) > 0 && \ + DT_PROP(DT_NODELABEL(can1), phase_seg2) > 0 .prop_ts1 = DT_PROP(DT_NODELABEL(can1), prop_seg) + - DT_PROP(DT_NODELABEL(can1), phase_seg1), + DT_PROP(DT_NODELABEL(can1), phase_seg1), .ts2 = DT_PROP(DT_NODELABEL(can1), phase_seg2), +#endif .pclken = { .enr = DT_CLOCKS_CELL(DT_NODELABEL(can1), bits), .bus = DT_CLOCKS_CELL(DT_NODELABEL(can1), bus), @@ -1154,10 +1220,16 @@ static const struct can_stm32_config can_stm32_cfg_2 = { .master_can = (CAN_TypeDef *)DT_PROP(DT_NODELABEL(can2), master_can_reg), .bus_speed = DT_PROP(DT_NODELABEL(can2), bus_speed), +#if DT_PROP(DT_NODELABEL(can2), sample_point) > 0 + .sample_point = DT_PROP(DT_NODELABEL(can2), sample_point), +#endif .sjw = DT_PROP(DT_NODELABEL(can2), sjw), +#if DT_PROP(DT_NODELABEL(can2), phase_seg1) > 0 && \ + DT_PROP(DT_NODELABEL(can2), phase_seg2) > 0 .prop_ts1 = DT_PROP(DT_NODELABEL(can2), prop_seg) + - DT_PROP(DT_NODELABEL(can2), phase_seg1), + DT_PROP(DT_NODELABEL(can2), phase_seg1), .ts2 = DT_PROP(DT_NODELABEL(can2), phase_seg2), +#endif .pclken = { .enr = DT_CLOCKS_CELL(DT_NODELABEL(can2), bits), .bus = DT_CLOCKS_CELL(DT_NODELABEL(can2), bus), diff --git a/drivers/can/can_stm32.h b/drivers/can/can_stm32.h index cae25fa0893a09..0e8241ca0a9ede 100644 --- a/drivers/can/can_stm32.h +++ b/drivers/can/can_stm32.h @@ -71,6 +71,7 @@ struct can_stm32_config { CAN_TypeDef *can; /*!< CAN Registers*/ CAN_TypeDef *master_can; /*!< CAN Registers for shared filter */ uint32_t bus_speed; + uint16_t sample_point; uint8_t sjw; uint8_t prop_ts1; uint8_t ts2; diff --git a/dts/bindings/can/can-controller.yaml b/dts/bindings/can/can-controller.yaml index 23c3ad77945a12..d774e535344584 100644 --- a/dts/bindings/can/can-controller.yaml +++ b/dts/bindings/can/can-controller.yaml @@ -23,13 +23,20 @@ properties: description: Resynchronization jump width (ISO 11898-1) prop-seg: type: int - required: true + required: false description: Time quantums of propagation segment (ISO 11898-1) phase-seg1: type: int - required: true + required: false description: Time quantums of phase buffer 1 segment (ISO 11898-1) phase-seg2: type: int - required: true + required: false description: Time quantums of phase buffer 2 segment (ISO 11898-1) + sample-point: + type: int + required: false + description: > + Sample point in permille. + This param is required if segments are not given. + If the sample point is given, the segments are ignored. diff --git a/dts/bindings/can/can-fd-controller.yaml b/dts/bindings/can/can-fd-controller.yaml new file mode 100644 index 00000000000000..9e6f09840a35c4 --- /dev/null +++ b/dts/bindings/can/can-fd-controller.yaml @@ -0,0 +1,32 @@ +# Common fields for CAN-FD controllers + +include: can-controller.yaml + +properties: + bus-speed-data: + type: int + required: true + description: data phase bus speed in Baud/s + sjw-data: + type: int + required: true + description: Resynchronization jump width for the data phase. (ISO11898-1:2015) + prop-seg-data: + type: int + required: false + description: Time quantums of propagation segment for the data phase. (ISO11898-1:2015) + phase-seg1-data: + type: int + required: false + description: Time quantums of phase buffer 1 segment for the data phase. (ISO11898-1:2015) + phase-seg2-data: + type: int + required: false + description: Time quantums of phase buffer 2 segment for the data phase. (ISO11898-1:2015) + sample-point-data: + type: int + required: false + description: > + Sample point in permille for the data phase. + This param is required if segments are not given. + If the sample point is given, the segments are ignored. diff --git a/include/drivers/can.h b/include/drivers/can.h index c6b13edbc626a9..b2047bff32588d 100644 --- a/include/drivers/can.h +++ b/include/drivers/can.h @@ -98,7 +98,7 @@ enum can_mode { /*Controller is in loopback mode (receive own messages)*/ CAN_LOOPBACK_MODE, /*Combination of loopback and silent*/ - CAN_SILENT_LOOPBACK_MODE + CAN_SILENT_LOOPBACK_MODE, }; /** @@ -236,6 +236,43 @@ struct can_bus_err_cnt { uint8_t rx_err_cnt; }; + +/** + * @brief canbus timings + * + * Used to pass bus timing values to the config and bitrate calculator function. + * + * The propagation segment represents the time of the signal propagation. + * Phase segment 1 and phase segment 2 define the sampling point. + * prop_seg and phase_seg1 affect the sampling-point in the same way and some + * controllers only have a register for the sum of those two. The sync segment + * always has a length of 1 tq + * +---------+----------+------------+------------+ + * |sync_seg | prop_seg | phase_seg1 | phase_seg2 | + * +---------+----------+------------+------------+ + * ^ + * Sampling-Point + * 1 tq (time quantum) has the length of 1/(core_clock / prescaler) + * The bitrate is defined by the core clock divided by the prescaler and the + * sum of the segments. + * br = (core_clock / prescaler) / (1 + prop_seg + phase_seg1 + phase_seg2) + * The resynchronization jump width (SJW) defines the amount of time quantum + * the sample point can be moved. + * The sample point is moved when resynchronization is needed. + */ +struct can_timing { + /** Synchronisation jump width*/ + uint16_t sjw; + /** Propagation Segment */ + uint16_t prop_seg; + /** Phase Segment 1 */ + uint16_t phase_seg1; + /** Phase Segment 2 */ + uint16_t phase_seg2; + /** Prescaler value */ + uint16_t prescaler; +}; + /** * @typedef can_tx_callback_t * @brief Define the application callback handler function signature @@ -265,8 +302,11 @@ typedef void (*can_rx_callback_t)(struct zcan_frame *msg, void *arg); typedef void(*can_state_change_isr_t)(enum can_state state, struct can_bus_err_cnt err_cnt); -typedef int (*can_configure_t)(const struct device *dev, enum can_mode mode, - uint32_t bitrate); +typedef int (*can_set_timing_t)(const struct device *dev, + const struct can_timing *timing, + const struct can_timing *timing_data); + +typedef int (*can_set_mode_t)(const struct device *dev, enum can_mode mode); typedef int (*can_send_t)(const struct device *dev, const struct zcan_frame *msg, @@ -293,6 +333,8 @@ typedef enum can_state (*can_get_state_t)(const struct device *dev, typedef void(*can_register_state_change_isr_t)(const struct device *dev, can_state_change_isr_t isr); +typedef int (*can_get_core_clock_t)(const struct device *dev, uint32_t *rate); + #ifndef CONFIG_CAN_WORKQ_FRAMES_BUF_CNT #define CONFIG_CAN_WORKQ_FRAMES_BUF_CNT 4 #endif @@ -316,7 +358,8 @@ struct zcan_work { }; __subsystem struct can_driver_api { - can_configure_t configure; + can_set_mode_t set_mode; + can_set_timing_t set_timing; can_send_t send; can_attach_isr_t attach_isr; can_detach_t detach; @@ -325,7 +368,17 @@ __subsystem struct can_driver_api { #endif can_get_state_t get_state; can_register_state_change_isr_t register_state_change_isr; - + can_get_core_clock_t get_core_clock; + /* Min values for the timing registers */ + struct can_timing timing_min; + /* Max values for the timing registers */ + struct can_timing timing_max; +#ifdef CONFIG_CAN_FD_MODE + /* Min values for the timing registers during the data phase */ + struct can_timing timing_min_data; + /* Max values for the timing registers during the data phase */ + struct can_timing timing_max_data; +#endif }; /** @@ -509,28 +562,196 @@ static inline void z_impl_can_detach(const struct device *dev, int filter_id) } /** - * @brief Configure operation of a host controller. + * @brief Read the core clock value + * + * Returns the core clock value. One time quantum is 1/core clock. * * @param dev Pointer to the device structure for the driver instance. - * @param mode Operation mode - * @param bitrate bus-speed in Baud/s + * @param[out] rate controller clock rate + * + * @retval 0 on success + * @retval negative on error + */ +__syscall int can_get_core_clock(const struct device *dev, uint32_t *rate); + +static inline int z_impl_can_get_core_clock(const struct device *dev, + uint32_t *rate) +{ + const struct can_driver_api *api = + (const struct can_driver_api *)dev->api; + + return api->get_core_clock(dev, rate); +} + +/** + * @brief Calculate timing parameters from bitrate and sample point + * + * Calculate the timing parameters from a given bitrate in bits/s and the + * sampling point in permill (1/1000) of the entire bit time. + * The bitrate must alway match perfectly. If no result can be given for the, + * give parameters, -EINVAL is returned. + * The sample_pnt does not always match perfectly. The algorithm tries to find + * the best match possible. + * + * @param dev Pointer to the device structure for the driver instance. + * @param res Result is written into the can_timing struct provided. + * @param bitrate Target bitrate in bits/s + * @param sample_pnt Sampling point in permill of the entire bit time. + * + * @retval Positive sample point error on success + * @retval -EINVAL if there is no solution for the desired values + * @retval -EIO if core_clock is not available + */ +int can_calc_timing(const struct device *dev, struct can_timing *res, + uint32_t bitrate, uint16_t sample_pnt); + +#ifdef CONFIG_CAN_FD_MODE +/** + * @brief Calculate timing parameters for the data phase + * + * Same as can_calc_timing but with the max and min values from the data phase. + * + * @param dev Pointer to the device structure for the driver instance. + * @param res Result is written into the can_timing struct provided. + * @param bitrate Target bitrate for the data phase in bits/s + * @param sample_pnt Sampling point data phase in permille of the entire bit time. + * + * @retval Positive sample point error on success + * @retval -EINVAL if there is no solution for the desired values + * @retval -EIO if core_clock is not available + */ +int can_calc_timing_data(const struct device *dev, struct can_timing *res, + uint32_t bitrate, uint16_t sample_pnt); +#endif + +/** + * @brief Fill in the prescaler value for a given bitrate and timing + * + * Fill the prescaler value in the timing struct. + * sjw, prop_seg, phase_seg1 and phase_seg2 must be given. + * The returned bitrate error is reminder of the devision of the clockrate by + * the bitrate times the timing segments. + * + * @param dev Pointer to the device structure for the driver instance. + * @param timing Result is written into the can_timing struct provided. + * @param bitrate Target bitrate. + * + * @retval bitrate error + * @retval negative on error + */ +int can_calc_prescaler(const struct device *dev, struct can_timing *timing, + uint32_t bitrate); + +/** + * @brief Set the controller to the given mode + * + * @param dev Pointer to the device structure for the driver instance. + * @param mode Operation mode * * @retval 0 If successful. * @retval -EIO General input / output error, failed to configure device. */ -__syscall int can_configure(const struct device *dev, enum can_mode mode, - uint32_t bitrate); +__syscall int can_set_mode(const struct device *dev, enum can_mode mode); -static inline int z_impl_can_configure(const struct device *dev, - enum can_mode mode, - uint32_t bitrate) +static inline int z_impl_can_set_mode(const struct device *dev, + enum can_mode mode) { const struct can_driver_api *api = (const struct can_driver_api *)dev->api; - return api->configure(dev, mode, bitrate); + return api->set_mode(dev, mode); } +/** + * @brief Configure timing of a host controller. + * + * The second parameter timing_data is only relevant for CAN-FD. + * If the controller does not support CAN-FD or the FD mode is not enabled, + * this parameter is ignored. + * + * @param dev Pointer to the device structure for the driver instance. + * @param timing Bus timings + * @param timing_data Bus timings for data phase (CAN-FD only) + * + * @retval 0 If successful. + * @retval -EIO General input / output error, failed to configure device. + */ +__syscall int can_set_timing(const struct device *dev, + const struct can_timing *timing, + const struct can_timing *timing_data); + +static inline int z_impl_can_set_timing(const struct device *dev, + const struct can_timing *timing, + const struct can_timing *timing_data) +{ + const struct can_driver_api *api = + (const struct can_driver_api *)dev->api; + + return api->set_timing(dev, timing, timing_data); +} + +/** + * @brief Set the bitrate of the CAN-FD controller + * + * The sample point is set to the CiA DS 301 reccommended value of 87.5% + * + * @param dev Pointer to the device structure for the driver instance. + * @param bitrate Desired arbitration phase bitrate + * @param bitrate_data Desired data phase bitrate + * + * @retval 0 If successful. + * @retval -EINVAL bitrate cannot be reached. + * @retval -EIO General input / output error, failed to set bitrate. + */ +static inline int can_set_bitrate(const struct device *dev, + uint32_t bitrate, + uint32_t bitrate_data) +{ + struct can_timing timing; +#ifdef CONFIG_CAN_FD_MODE + struct can_timing timing_data; +#endif + int ret; + + ret = can_calc_timing(dev, &timing, bitrate, 875); + if (ret < 0) { + return -EINVAL; + } + +#ifdef CONFIG_CAN_FD_MODE + ret = can_calc_timing_data(dev, &timing_data, bitrate_data, 875); + if (ret < 0) { + return -EINVAL; + } + + return can_set_timing(dev, &timing, &timing_data); +#else + return can_set_timing(dev, &timing, NULL); +#endif /* CONFIG_CAN_FD_MODE */ +} + +/** + * @brief Configure operation of a host controller. + * + * @param dev Pointer to the device structure for the driver instance. + * @param mode Operation mode + * @param bitrate bus-speed in Baud/s + * + * @retval 0 If successful. + * @retval -EIO General input / output error, failed to configure device. + */ +static inline int can_configure(const struct device *dev, enum can_mode mode, + uint32_t bitrate) +{ + int err = can_set_bitrate(dev, bitrate, 0); + if (err != 0) { + return err; + } + + return can_set_mode(dev, mode); +} + + /** * @brief Get current state * diff --git a/samples/drivers/can/src/main.c b/samples/drivers/can/src/main.c index 5953c780a4b7cc..ebebad74b46a1e 100644 --- a/samples/drivers/can/src/main.c +++ b/samples/drivers/can/src/main.c @@ -206,7 +206,7 @@ void main(void) } #ifdef CONFIG_LOOPBACK_MODE - can_configure(can_dev, CAN_LOOPBACK_MODE, 125000); + can_set_mode(can_dev, CAN_LOOPBACK_MODE); #endif #if DT_PHA_HAS_CELL(DT_ALIAS(led0), gpios, pin) && \ diff --git a/subsys/canbus/canopen/CO_driver.c b/subsys/canbus/canopen/CO_driver.c index 1850053a27364c..1921c9d84d3a46 100644 --- a/subsys/canbus/canopen/CO_driver.c +++ b/subsys/canbus/canopen/CO_driver.c @@ -212,7 +212,13 @@ CO_ReturnError_t CO_CANmodule_init(CO_CANmodule_t *CANmodule, txArray[i].bufferFull = false; } - err = can_configure(CANmodule->dev, CAN_NORMAL_MODE, KHZ(CANbitRate)); + err = can_set_bitrate(CANmodule->dev, KHZ(CANbitRate), 0); + if (err) { + LOG_ERR("failed to configure CAN bitrate (err %d)", err); + return CO_ERROR_ILLEGAL_ARGUMENT; + } + + err = can_set_mode(CANmodule->dev, CAN_NORMAL_MODE); if (err) { LOG_ERR("failed to configure CAN interface (err %d)", err); return CO_ERROR_ILLEGAL_ARGUMENT; diff --git a/tests/drivers/can/api/src/main.c b/tests/drivers/can/api/src/main.c index bb5afc848815ca..ba085c7a6498d9 100644 --- a/tests/drivers/can/api/src/main.c +++ b/tests/drivers/can/api/src/main.c @@ -414,7 +414,7 @@ static void test_set_loopback(void) { int ret; - ret = can_configure(can_dev, CAN_LOOPBACK_MODE, 0); + ret = can_set_mode(can_dev, CAN_LOOPBACK_MODE); zassert_equal(ret, 0, "Can't set loopback-mode. Err: %d", ret); } diff --git a/tests/drivers/can/stm32/src/main.c b/tests/drivers/can/stm32/src/main.c index 7d2bf76c0667fc..a385d66099b05d 100644 --- a/tests/drivers/can/stm32/src/main.c +++ b/tests/drivers/can/stm32/src/main.c @@ -133,7 +133,7 @@ static void test_filter_handling(void) can_dev = device_get_binding(DT_CHOSEN_ZEPHYR_CAN_PRIMARY_LABEL); - ret = can_configure(can_dev, CAN_LOOPBACK_MODE, 0); + ret = can_set_mode(can_dev, CAN_LOOPBACK_MODE); filter_id_1 = can_attach_msgq(can_dev, &can_msgq, &test_ext_masked_filter); zassert_not_equal(filter_id_1, CAN_NO_FREE_FILTER, diff --git a/tests/subsys/canbus/isotp/conformance/src/main.c b/tests/subsys/canbus/isotp/conformance/src/main.c index 2461197c8b4654..1e695291bb3fbe 100644 --- a/tests/subsys/canbus/isotp/conformance/src/main.c +++ b/tests/subsys/canbus/isotp/conformance/src/main.c @@ -882,7 +882,7 @@ void test_main(void) can_dev = device_get_binding(CAN_DEVICE_NAME); zassert_not_null(can_dev, "CAN device not not found"); - ret = can_configure(can_dev, CAN_LOOPBACK_MODE, 0); + ret = can_set_mode(can_dev, CAN_LOOPBACK_MODE); zassert_equal(ret, 0, "Failed to set loopback mode [%d]", ret); k_sem_init(&send_compl_sem, 0, 1); diff --git a/tests/subsys/canbus/isotp/implementation/src/main.c b/tests/subsys/canbus/isotp/implementation/src/main.c index 205ab97c5e0a38..d53d1ee25e2aa4 100644 --- a/tests/subsys/canbus/isotp/implementation/src/main.c +++ b/tests/subsys/canbus/isotp/implementation/src/main.c @@ -423,7 +423,7 @@ void test_main(void) can_dev = device_get_binding(CAN_DEVICE_NAME); zassert_not_null(can_dev, "CAN device not not found"); - ret = can_configure(can_dev, CAN_LOOPBACK_MODE, 0); + ret = can_set_mode(can_dev, CAN_LOOPBACK_MODE); zassert_equal(ret, 0, "Configuring loopback mode failed (%d)", ret); ztest_test_suite(isotp,