drivers: can: Rework can_configure API
The previous API can't change the sampling-point and only allowed bitrates that fit the time segments. The new API allows for shifting the sampling-point and adjusts the number of time quantum in a bit to all more possible bitrates. The functions to calculate the timings are moved to the can_common file. They can be used for all drivers. Signed-off-by: Alexander Wachter <alexander@wachter.cloud>
This commit is contained in:
parent
caa35d1a3a
commit
8b6c1bd4b7
|
@ -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
|
||||
|
|
|
@ -11,6 +11,9 @@
|
|||
#include <logging/log.h>
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -7,18 +7,39 @@
|
|||
#include <syscall_handler.h>
|
||||
#include <drivers/can.h>
|
||||
|
||||
static inline int z_vrfy_can_configure(const struct device *dev,
|
||||
enum can_mode mode,
|
||||
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));
|
||||
|
||||
return z_impl_can_set_timing((const struct device *)dev,
|
||||
(const struct can_timing *)timing,
|
||||
(const struct can_timing *)timing_data);
|
||||
}
|
||||
#include <syscalls/can_set_timing_mrsh.c>
|
||||
|
||||
static inline int z_vrfy_can_set_bitrate(const struct device *dev,
|
||||
uint32_t bitrate)
|
||||
{
|
||||
|
||||
Z_OOPS(Z_SYSCALL_DRIVER_CAN(dev, configure));
|
||||
Z_OOPS(Z_SYSCALL_DRIVER_CAN(dev, set_bitrate));
|
||||
|
||||
return z_impl_can_configure((const struct device *)dev,
|
||||
(enum can_mode)mode,
|
||||
return z_impl_can_set_bitrate((const struct device *)dev,
|
||||
(uint32_t)bitrate);
|
||||
}
|
||||
#include <syscalls/can_configure_mrsh.c>
|
||||
#include <syscalls/can_set_bitrate_mrsh.c>
|
||||
|
||||
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_get_core_clock(dev, rate);
|
||||
}
|
||||
#include <syscalls/can_get_core_clock_mrsh.c>
|
||||
|
||||
static inline int z_vrfy_can_send(const struct device *dev,
|
||||
const struct zcan_frame *msg,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,7 +408,19 @@ static int mcp2515_configure(const struct device *dev, enum can_mode mode,
|
|||
}
|
||||
|
||||
done:
|
||||
ret = mcp2515_set_mode(dev,
|
||||
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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 -EIO;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (clock_control_get_rate(clock_dev, config->clock_subsys,
|
||||
&clock_freq)) {
|
||||
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) \
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -290,69 +290,14 @@ 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;
|
||||
}
|
||||
|
||||
if (!bitrate) {
|
||||
bitrate = cfg->bus_speed;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
__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;
|
||||
|
||||
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;
|
||||
LOG_DBG("Set mode %d", mode);
|
||||
|
||||
k_mutex_lock(&data->inst_mutex, K_FOREVER);
|
||||
ret = can_enter_init_mode(can);
|
||||
|
@ -361,26 +306,97 @@ int can_stm32_runtime_configure(const struct device *dev, enum can_mode mode,
|
|||
goto done;
|
||||
}
|
||||
|
||||
can->BTR = reg_mode | sjw | ts1 | ts2 | (prescaler - 1U);
|
||||
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;
|
||||
}
|
||||
|
||||
done:
|
||||
ret = can_leave_init_mode(can);
|
||||
if (ret) {
|
||||
LOG_ERR("Failed to leave init mode");
|
||||
}
|
||||
k_mutex_unlock(&data->inst_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
ARG_UNUSED(timing_data);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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_runtime_configure(dev, CAN_NORMAL_MODE, 0);
|
||||
ret = can_stm32_set_timing(dev, &timing, NULL);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
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),
|
||||
.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),
|
||||
.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),
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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.
|
||||
|
|
32
dts/bindings/can/can-fd-controller.yaml
Normal file
32
dts/bindings/can/can-fd-controller.yaml
Normal file
|
@ -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.
|
|
@ -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
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -508,6 +561,175 @@ static inline void z_impl_can_detach(const struct device *dev, int filter_id)
|
|||
return api->detach(dev, filter_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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[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_set_mode(const struct device *dev, enum can_mode mode);
|
||||
|
||||
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->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.
|
||||
*
|
||||
|
@ -518,19 +740,18 @@ static inline void z_impl_can_detach(const struct device *dev, int filter_id)
|
|||
* @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);
|
||||
|
||||
static inline int z_impl_can_configure(const struct device *dev,
|
||||
enum can_mode mode,
|
||||
static inline int can_configure(const struct device *dev, enum can_mode mode,
|
||||
uint32_t bitrate)
|
||||
{
|
||||
const struct can_driver_api *api =
|
||||
(const struct can_driver_api *)dev->api;
|
||||
|
||||
return api->configure(dev, mode, bitrate);
|
||||
int err = can_set_bitrate(dev, bitrate, 0);
|
||||
if (err != 0) {
|
||||
return err;
|
||||
}
|
||||
|
||||
return can_set_mode(dev, mode);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get current state
|
||||
*
|
||||
|
|
|
@ -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) && \
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in a new issue