can-calc-bit-timing: add support to decode user supplied bit timing parameters

Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
pull/277/head
Marc Kleine-Budde 2021-01-17 19:31:27 +01:00
parent ef8cea79f0
commit 913311fc15
1 changed files with 152 additions and 19 deletions

View File

@ -2,7 +2,7 @@
/* can-calc-bit-timing.c: Calculate CAN bit timing parameters
*
* Copyright (C) 2008 Wolfgang Grandegger <wg@grandegger.com>
* Copyright (C) 2016 Marc Kleine-Budde <mkl@pengutronix.de>
* Copyright (C) 2016, 2021 Marc Kleine-Budde <mkl@pengutronix.de>
*
* Derived from:
* can_baud.c - CAN baudrate calculation
@ -29,6 +29,17 @@
#include <linux/can/netlink.h>
#include <linux/types.h>
enum {
OPT_TQ = UCHAR_MAX + 1,
OPT_PROP_SEG,
OPT_PHASE_SEG1,
OPT_PHASE_SEG2,
OPT_SJW,
OPT_BRP,
OPT_TSEG1,
OPT_TSEG2,
};
/* imported from kernel */
/**
@ -140,7 +151,17 @@ static void print_usage(char *cmd)
"\t-b <bitrate> bit-rate in bits/sec\n"
"\t-s <samp_pt> sample-point in one-tenth of a percent\n"
"\t or 0 for CIA recommended sample points\n"
"\t-c <clock> real CAN system clock in Hz\n",
"\t-c <clock> real CAN system clock in Hz\n"
"\n"
"Or supply low level bit timing parameters to decode them:\n"
"\n"
"\t--prop-seg Propagation segment in TQs\n"
"\t--phase-seg1 Phase buffer segment 1 in TQs\n"
"\t--phase-seg2 Phase buffer segment 2 in TQs\n"
"\t--sjw Synchronisation jump width in TQs\n"
"\t--brp Bit-rate prescaler\n"
"\t--tseg1 Time segment 1 = prop-seg + phase-seg1\n"
"\t--tseg2 Time segment 2 = phase_seg2\n",
cmd);
}
@ -569,6 +590,46 @@ static int can_calc_bittiming(struct net_device *dev, struct can_bittiming *bt,
return 0;
}
static int can_fixup_bittiming(struct net_device *dev, struct can_bittiming *bt,
const struct can_bittiming_const *btc)
{
struct can_priv *priv = netdev_priv(dev);
int tseg1, alltseg;
u64 brp64, v64;
tseg1 = bt->prop_seg + bt->phase_seg1;
if (!bt->sjw)
bt->sjw = 1;
if (bt->sjw > btc->sjw_max ||
tseg1 < btc->tseg1_min || tseg1 > btc->tseg1_max ||
bt->phase_seg2 < btc->tseg2_min || bt->phase_seg2 > btc->tseg2_max)
return -ERANGE;
if (!bt->brp) {
brp64 = (u64)priv->clock.freq * (u64)bt->tq;
if (btc->brp_inc > 1)
do_div(brp64, btc->brp_inc);
brp64 += 500000000UL - 1;
do_div(brp64, 1000000000UL); /* the practicable BRP */
if (btc->brp_inc > 1)
brp64 *= btc->brp_inc;
bt->brp = brp64;
}
v64 = bt->brp * 1000 * 1000 * 1000;
do_div(v64, priv->clock.freq);
bt->tq = v64;
if (bt->brp < btc->brp_min || bt->brp > btc->brp_max)
return -EINVAL;
alltseg = CAN_CALC_SYNC_SEG + bt->prop_seg + bt->phase_seg1 + bt->phase_seg2;
bt->bitrate = priv->clock.freq / (bt->brp * alltseg);
bt->sample_point = ((CAN_CALC_SYNC_SEG + tseg1) * 1000) / alltseg;
return 0;
}
static __u32 get_cia_sample_point(__u32 bitrate)
{
__u32 sampl_pt;
@ -584,6 +645,7 @@ static __u32 get_cia_sample_point(__u32 bitrate)
}
static void print_bit_timing(const struct calc_bittiming_const *btc,
const struct can_bittiming *ref_bt,
const struct calc_ref_clk *ref_clk,
unsigned int bitrate_nominal,
unsigned int spt_nominal,
@ -613,9 +675,18 @@ static void print_bit_timing(const struct calc_bittiming_const *btc,
printf("\n");
}
if (can_calc_bittiming(&dev, &bt, &btc->bittiming_const)) {
printf("%7d ***bitrate not possible***\n", bitrate_nominal);
return;
if (ref_bt) {
bt = *ref_bt;
if (can_fixup_bittiming(&dev, &bt, &btc->bittiming_const)) {
printf("%7d ***parameters exceed controller's range***\n", bitrate_nominal);
return;
}
} else {
if (can_calc_bittiming(&dev, &bt, &btc->bittiming_const)) {
printf("%7d ***bitrate not possible***\n", bitrate_nominal);
return;
}
}
/* get nominal sample point */
@ -625,21 +696,30 @@ static void print_bit_timing(const struct calc_bittiming_const *btc,
rate_error = abs(bitrate_nominal - bt.bitrate);
spt_error = abs(spt_nominal - bt.sample_point);
printf("%7d "
"%6d %3d %4d %4d "
"%3d %3d "
"%7d %4.1f%% "
"%4.1f%% %4.1f%% %4.1f%% ",
printf("%7d " /* Bitrate */
"%6d %3d %4d %4d " /* TQ[ns], PrS, PhS1, PhS2 */
"%3d %3d " /* SJW, BRP */
"%7d ", /* real Bitrate */
bitrate_nominal,
bt.tq, bt.prop_seg, bt.phase_seg1, bt.phase_seg2,
bt.sjw, bt.brp,
bt.bitrate);
bt.bitrate,
100.0 * rate_error / bitrate_nominal,
if (100.0 * rate_error / bitrate_nominal > 99.9)
printf("≥100%% ");
else
printf("%4.1f%% ",
100.0 * rate_error / bitrate_nominal);
printf("%4.1f%% %4.1f%% ", /* nom SampP, real SampP */
spt_nominal / 10.0,
bt.sample_point / 10.0,
100.0 * spt_error / spt_nominal);
bt.sample_point / 10.0);
if (100.0 * spt_error / spt_nominal > 99.9)
printf("≥100%% ");
else
printf("%4.1f%% ", /* SampP Error */
100.0 * spt_error / spt_nominal);
if (btc->printf_btr)
btc->printf_btr(&bt, false);
@ -655,6 +735,7 @@ static void do_list(void)
}
static void do_calc(const char *name,
const struct can_bittiming *opt_ref_bt,
__u32 bitrate_nominal, unsigned int spt_nominal,
struct calc_ref_clk *opt_ref_clk, bool quiet)
{
@ -681,11 +762,11 @@ static void do_calc(const char *name,
break;
if (bitrate_nominal) {
print_bit_timing(btc, ref_clk, bitrate_nominal,
print_bit_timing(btc, opt_ref_bt, ref_clk, bitrate_nominal,
spt_nominal, quiet);
} else {
for (k = 0; k < ARRAY_SIZE(common_bitrates); k++)
print_bit_timing(btc, ref_clk,
print_bit_timing(btc, opt_ref_bt, ref_clk,
common_bitrates[k],
spt_nominal, k);
}
@ -710,12 +791,24 @@ int main(int argc, char *argv[])
struct calc_ref_clk opt_ref_clk = {
.name = "cmd-line",
};
struct can_bittiming bt = { 0 };
bool quiet = false, list = false;
const char *name = NULL;
int opt;
const struct option long_options[] = {
{ "tq", required_argument, 0, OPT_TQ, },
{ "prop-seg", required_argument, 0, OPT_PROP_SEG, },
{ "phase-seg1", required_argument, 0, OPT_PHASE_SEG1, },
{ "phase-seg2", required_argument, 0, OPT_PHASE_SEG2, },
{ "sjw", required_argument, 0, OPT_SJW, },
{ "brp", required_argument, 0, OPT_BRP, },
{ "tseg1", required_argument, 0, OPT_TSEG1, },
{ "tseg2", required_argument, 0, OPT_TSEG2, },
{ 0, 0, 0, 0 },
};
while ((opt = getopt(argc, argv, "b:c:lqs:?")) != -1) {
while ((opt = getopt_long(argc, argv, "b:c:lqs:?", long_options, NULL)) != -1) {
switch (opt) {
case 'b':
bitrate_nominal = atoi(optarg);
@ -742,6 +835,43 @@ int main(int argc, char *argv[])
exit(EXIT_SUCCESS);
break;
case OPT_TQ:
bt.tq = strtoul(optarg, NULL, 10);
break;
case OPT_PROP_SEG:
bt.prop_seg = strtoul(optarg, NULL, 10);
break;
case OPT_PHASE_SEG1:
bt.phase_seg1 = strtoul(optarg, NULL, 10);
break;
case OPT_PHASE_SEG2:
bt.phase_seg2 = strtoul(optarg, NULL, 10);
break;
case OPT_SJW:
bt.sjw = strtoul(optarg, NULL, 10);
break;
case OPT_BRP:
bt.brp = strtoul(optarg, NULL, 10);
break;
case OPT_TSEG1: {
__u32 tseg1;
tseg1 = strtoul(optarg, NULL, 10);
bt.prop_seg = tseg1 / 2;
bt.phase_seg1 = tseg1 - bt.prop_seg;
break;
}
case OPT_TSEG2:
bt.phase_seg2 = strtoul(optarg, NULL, 10);
break;
default:
print_usage(basename(argv[0]));
exit(EXIT_FAILURE);
@ -767,8 +897,11 @@ int main(int argc, char *argv[])
exit(EXIT_FAILURE);
}
do_calc(name, bitrate_nominal, spt_nominal,
opt_ref_clk.clk ? &opt_ref_clk : NULL, quiet);
do_calc(name,
bt.prop_seg ? &bt: NULL,
bitrate_nominal, spt_nominal,
opt_ref_clk.clk ? &opt_ref_clk : NULL,
quiet);
exit(EXIT_SUCCESS);
}