Skip to content

Commit

Permalink
drivers: can: Rework can_configure API
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
alexanderwachter authored and carlescufi committed Dec 17, 2020
1 parent caa35d1 commit 8b6c1bd
Show file tree
Hide file tree
Showing 19 changed files with 844 additions and 154 deletions.
5 changes: 5 additions & 0 deletions drivers/can/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
144 changes: 144 additions & 0 deletions drivers/can/can_common.c
Original file line number Diff line number Diff line change
Expand Up @@ -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))

Expand Down Expand Up @@ -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);
}
37 changes: 29 additions & 8 deletions drivers/can/can_handlers.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
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 <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, set_bitrate));

return z_impl_can_set_bitrate((const struct device *)dev,
(uint32_t)bitrate);
}
#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_configure((const struct device *)dev,
(enum can_mode)mode,
(uint32_t)bitrate);
return z_impl_can_get_core_clock(dev, rate);
}
#include <syscalls/can_configure_mrsh.c>
#include <syscalls/can_get_core_clock_mrsh.c>

static inline int z_vrfy_can_send(const struct device *dev,
const struct zcan_frame *msg,
Expand Down
43 changes: 36 additions & 7 deletions drivers/can/can_loopback.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down Expand Up @@ -234,19 +241,41 @@ 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,
.get_state = can_loopback_get_state,
#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);
Expand Down
Loading

0 comments on commit 8b6c1bd

Please sign in to comment.