diff options
Diffstat (limited to 'drivers/net/can/dev/bittiming.c')
-rw-r--r-- | drivers/net/can/dev/bittiming.c | 120 |
1 files changed, 93 insertions, 27 deletions
diff --git a/drivers/net/can/dev/bittiming.c b/drivers/net/can/dev/bittiming.c index 7ae80763c960..0b93900b1dfa 100644 --- a/drivers/net/can/dev/bittiming.c +++ b/drivers/net/can/dev/bittiming.c @@ -6,25 +6,81 @@ #include <linux/can/dev.h> +void can_sjw_set_default(struct can_bittiming *bt) +{ + if (bt->sjw) + return; + + /* If user space provides no sjw, use sane default of phase_seg2 / 2 */ + bt->sjw = max(1U, min(bt->phase_seg1, bt->phase_seg2 / 2)); +} + +int can_sjw_check(const struct net_device *dev, const struct can_bittiming *bt, + const struct can_bittiming_const *btc, struct netlink_ext_ack *extack) +{ + if (bt->sjw > btc->sjw_max) { + NL_SET_ERR_MSG_FMT(extack, "sjw: %u greater than max sjw: %u", + bt->sjw, btc->sjw_max); + return -EINVAL; + } + + if (bt->sjw > bt->phase_seg1) { + NL_SET_ERR_MSG_FMT(extack, + "sjw: %u greater than phase-seg1: %u", + bt->sjw, bt->phase_seg1); + return -EINVAL; + } + + if (bt->sjw > bt->phase_seg2) { + NL_SET_ERR_MSG_FMT(extack, + "sjw: %u greater than phase-seg2: %u", + bt->sjw, bt->phase_seg2); + return -EINVAL; + } + + return 0; +} + /* Checks the validity of the specified bit-timing parameters prop_seg, * phase_seg1, phase_seg2 and sjw and tries to determine the bitrate * prescaler value brp. You can find more information in the header * file linux/can/netlink.h. */ static int can_fixup_bittiming(const struct net_device *dev, struct can_bittiming *bt, - const struct can_bittiming_const *btc) + const struct can_bittiming_const *btc, + struct netlink_ext_ack *extack) { + const unsigned int tseg1 = bt->prop_seg + bt->phase_seg1; const struct can_priv *priv = netdev_priv(dev); - unsigned int tseg1, alltseg; u64 brp64; + int err; - 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 (tseg1 < btc->tseg1_min) { + NL_SET_ERR_MSG_FMT(extack, "prop-seg + phase-seg1: %u less than tseg1-min: %u", + tseg1, btc->tseg1_min); + return -EINVAL; + } + if (tseg1 > btc->tseg1_max) { + NL_SET_ERR_MSG_FMT(extack, "prop-seg + phase-seg1: %u greater than tseg1-max: %u", + tseg1, btc->tseg1_max); + return -EINVAL; + } + if (bt->phase_seg2 < btc->tseg2_min) { + NL_SET_ERR_MSG_FMT(extack, "phase-seg2: %u less than tseg2-min: %u", + bt->phase_seg2, btc->tseg2_min); + return -EINVAL; + } + if (bt->phase_seg2 > btc->tseg2_max) { + NL_SET_ERR_MSG_FMT(extack, "phase-seg2: %u greater than tseg2-max: %u", + bt->phase_seg2, btc->tseg2_max); + return -EINVAL; + } + + can_sjw_set_default(bt); + + err = can_sjw_check(dev, bt, btc, extack); + if (err) + return err; brp64 = (u64)priv->clock.freq * (u64)bt->tq; if (btc->brp_inc > 1) @@ -35,12 +91,21 @@ static int can_fixup_bittiming(const struct net_device *dev, struct can_bittimin brp64 *= btc->brp_inc; bt->brp = (u32)brp64; - if (bt->brp < btc->brp_min || bt->brp > btc->brp_max) + if (bt->brp < btc->brp_min) { + NL_SET_ERR_MSG_FMT(extack, "resulting brp: %u less than brp-min: %u", + bt->brp, btc->brp_min); + return -EINVAL; + } + if (bt->brp > btc->brp_max) { + NL_SET_ERR_MSG_FMT(extack, "resulting brp: %u greater than brp-max: %u", + bt->brp, btc->brp_max); return -EINVAL; + } - alltseg = bt->prop_seg + bt->phase_seg1 + bt->phase_seg2 + 1; - bt->bitrate = priv->clock.freq / (bt->brp * alltseg); - bt->sample_point = ((tseg1 + 1) * 1000) / alltseg; + bt->bitrate = priv->clock.freq / (bt->brp * can_bit_time(bt)); + bt->sample_point = ((CAN_SYNC_SEG + tseg1) * 1000) / can_bit_time(bt); + bt->tq = DIV_U64_ROUND_CLOSEST(mul_u32_u32(bt->brp, NSEC_PER_SEC), + priv->clock.freq); return 0; } @@ -49,7 +114,8 @@ static int can_fixup_bittiming(const struct net_device *dev, struct can_bittimin static int can_validate_bitrate(const struct net_device *dev, const struct can_bittiming *bt, const u32 *bitrate_const, - const unsigned int bitrate_const_cnt) + const unsigned int bitrate_const_cnt, + struct netlink_ext_ack *extack) { unsigned int i; @@ -58,30 +124,30 @@ can_validate_bitrate(const struct net_device *dev, const struct can_bittiming *b return 0; } + NL_SET_ERR_MSG_FMT(extack, "bitrate %u bps not supported", + bt->brp); + return -EINVAL; } int can_get_bittiming(const struct net_device *dev, struct can_bittiming *bt, const struct can_bittiming_const *btc, const u32 *bitrate_const, - const unsigned int bitrate_const_cnt) + const unsigned int bitrate_const_cnt, + struct netlink_ext_ack *extack) { - int err; - /* Depending on the given can_bittiming parameter structure the CAN * timing parameters are calculated based on the provided bitrate OR * alternatively the CAN timing parameters (tq, prop_seg, etc.) are * provided directly which are then checked and fixed up. */ if (!bt->tq && bt->bitrate && btc) - err = can_calc_bittiming(dev, bt, btc); - else if (bt->tq && !bt->bitrate && btc) - err = can_fixup_bittiming(dev, bt, btc); - else if (!bt->tq && bt->bitrate && bitrate_const) - err = can_validate_bitrate(dev, bt, bitrate_const, - bitrate_const_cnt); - else - err = -EINVAL; - - return err; + return can_calc_bittiming(dev, bt, btc, extack); + if (bt->tq && !bt->bitrate && btc) + return can_fixup_bittiming(dev, bt, btc, extack); + if (!bt->tq && bt->bitrate && bitrate_const) + return can_validate_bitrate(dev, bt, bitrate_const, + bitrate_const_cnt, extack); + + return -EINVAL; } |