drivers: can: change sample point for can_set_bitrate() at high bitrates

CAN in Automation (CiA) 301 v4.2.0 recommends a sample point location of
87.5% percent for all bitrates. However, some CAN controllers have
difficulties meeting this for higher bitrates.

Change can_set_bitrate() to use a sample point of 75.0% for bitrates
over 800 kbit/s, 80.0% for bitrates over 500 kbit/s, and 87.5% for all
other bitrates. This is in line with the sample point locations used by
the Linux kernel.

Regard a sample point error of more than +/- 5.0% as an error in setting
the bitrate. Previously, any sample rate error was accepted without
providing any feedback to the caller. This is in line with the CAN
sample point calculation criteria used by the Linux kernel.

Signed-off-by: Henrik Brix Andersen <hebad@vestas.com>
This commit is contained in:
Henrik Brix Andersen 2022-04-06 17:22:46 +02:00 committed by Marti Bolivar
commit fba27b4bde
2 changed files with 49 additions and 4 deletions

View file

@ -11,6 +11,9 @@
LOG_MODULE_REGISTER(can_common, CONFIG_CAN_LOG_LEVEL);
/* Maximum acceptable deviation in sample point location (permille) */
#define SAMPLE_POINT_MARGIN 50
/* CAN sync segment is always one time quantum */
#define CAN_SYNC_SEG 1
@ -180,6 +183,30 @@ int can_calc_prescaler(const struct device *dev, struct can_timing *timing,
return core_clock % (ts * timing->prescaler);
}
/**
* @brief Get the sample point location for a given bitrate
*
* @param bitrate The bitrate in bits/second.
* @return The sample point in permille.
*/
uint16_t sample_point_for_bitrate(uint32_t bitrate)
{
uint16_t sample_pnt;
if (bitrate > 800000) {
/* 75.0% */
sample_pnt = 750;
} else if (bitrate > 500000) {
/* 80.0% */
sample_pnt = 800;
} else {
/* 87.5% */
sample_pnt = 875;
}
return sample_pnt;
}
int can_set_bitrate(const struct device *dev, uint32_t bitrate, uint32_t bitrate_data)
{
struct can_timing timing;
@ -187,6 +214,7 @@ int can_set_bitrate(const struct device *dev, uint32_t bitrate, uint32_t bitrate
struct can_timing timing_data;
#endif /* CONFIG_CAN_FD_MODE */
uint32_t max_bitrate;
uint16_t sample_pnt;
int ret;
ret = can_get_max_bitrate(dev, &max_bitrate);
@ -201,11 +229,16 @@ int can_set_bitrate(const struct device *dev, uint32_t bitrate, uint32_t bitrate
return -ENOTSUP;
}
ret = can_calc_timing(dev, &timing, bitrate, 875);
sample_pnt = sample_point_for_bitrate(bitrate);
ret = can_calc_timing(dev, &timing, bitrate, sample_pnt);
if (ret < 0) {
return -EINVAL;
}
if (ret > SAMPLE_POINT_MARGIN) {
return -EINVAL;
}
timing.sjw = CAN_SJW_NO_CHANGE;
#ifdef CONFIG_CAN_FD_MODE
@ -213,11 +246,16 @@ int can_set_bitrate(const struct device *dev, uint32_t bitrate, uint32_t bitrate
return -ENOTSUP;
}
ret = can_calc_timing_data(dev, &timing_data, bitrate_data, 875);
sample_pnt = sample_point_for_bitrate(bitrate_data);
ret = can_calc_timing_data(dev, &timing_data, bitrate_data, sample_pnt);
if (ret < 0) {
return -EINVAL;
}
if (ret > SAMPLE_POINT_MARGIN) {
return -EINVAL;
}
timing_data.sjw = CAN_SJW_NO_CHANGE;
return can_set_timing(dev, &timing, &timing_data);