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:
Alexander Wachter 2020-04-27 18:58:05 +02:00 committed by Carles Cufí
commit 8b6c1bd4b7
19 changed files with 852 additions and 162 deletions

View file

@ -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),
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),