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;  }  |