162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/* Copyright (C) 2005 Marc Kleine-Budde, Pengutronix
362306a36Sopenharmony_ci * Copyright (C) 2006 Andrey Volkov, Varma Electronics
462306a36Sopenharmony_ci * Copyright (C) 2008-2009 Wolfgang Grandegger <wg@grandegger.com>
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <linux/can/dev.h>
862306a36Sopenharmony_ci
962306a36Sopenharmony_civoid can_sjw_set_default(struct can_bittiming *bt)
1062306a36Sopenharmony_ci{
1162306a36Sopenharmony_ci	if (bt->sjw)
1262306a36Sopenharmony_ci		return;
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci	/* If user space provides no sjw, use sane default of phase_seg2 / 2 */
1562306a36Sopenharmony_ci	bt->sjw = max(1U, min(bt->phase_seg1, bt->phase_seg2 / 2));
1662306a36Sopenharmony_ci}
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ciint can_sjw_check(const struct net_device *dev, const struct can_bittiming *bt,
1962306a36Sopenharmony_ci		  const struct can_bittiming_const *btc, struct netlink_ext_ack *extack)
2062306a36Sopenharmony_ci{
2162306a36Sopenharmony_ci	if (bt->sjw > btc->sjw_max) {
2262306a36Sopenharmony_ci		NL_SET_ERR_MSG_FMT(extack, "sjw: %u greater than max sjw: %u",
2362306a36Sopenharmony_ci				   bt->sjw, btc->sjw_max);
2462306a36Sopenharmony_ci		return -EINVAL;
2562306a36Sopenharmony_ci	}
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci	if (bt->sjw > bt->phase_seg1) {
2862306a36Sopenharmony_ci		NL_SET_ERR_MSG_FMT(extack,
2962306a36Sopenharmony_ci				   "sjw: %u greater than phase-seg1: %u",
3062306a36Sopenharmony_ci				   bt->sjw, bt->phase_seg1);
3162306a36Sopenharmony_ci		return -EINVAL;
3262306a36Sopenharmony_ci	}
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci	if (bt->sjw > bt->phase_seg2) {
3562306a36Sopenharmony_ci		NL_SET_ERR_MSG_FMT(extack,
3662306a36Sopenharmony_ci				   "sjw: %u greater than phase-seg2: %u",
3762306a36Sopenharmony_ci				   bt->sjw, bt->phase_seg2);
3862306a36Sopenharmony_ci		return -EINVAL;
3962306a36Sopenharmony_ci	}
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	return 0;
4262306a36Sopenharmony_ci}
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci/* Checks the validity of the specified bit-timing parameters prop_seg,
4562306a36Sopenharmony_ci * phase_seg1, phase_seg2 and sjw and tries to determine the bitrate
4662306a36Sopenharmony_ci * prescaler value brp. You can find more information in the header
4762306a36Sopenharmony_ci * file linux/can/netlink.h.
4862306a36Sopenharmony_ci */
4962306a36Sopenharmony_cistatic int can_fixup_bittiming(const struct net_device *dev, struct can_bittiming *bt,
5062306a36Sopenharmony_ci			       const struct can_bittiming_const *btc,
5162306a36Sopenharmony_ci			       struct netlink_ext_ack *extack)
5262306a36Sopenharmony_ci{
5362306a36Sopenharmony_ci	const unsigned int tseg1 = bt->prop_seg + bt->phase_seg1;
5462306a36Sopenharmony_ci	const struct can_priv *priv = netdev_priv(dev);
5562306a36Sopenharmony_ci	u64 brp64;
5662306a36Sopenharmony_ci	int err;
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	if (tseg1 < btc->tseg1_min) {
5962306a36Sopenharmony_ci		NL_SET_ERR_MSG_FMT(extack, "prop-seg + phase-seg1: %u less than tseg1-min: %u",
6062306a36Sopenharmony_ci				   tseg1, btc->tseg1_min);
6162306a36Sopenharmony_ci		return -EINVAL;
6262306a36Sopenharmony_ci	}
6362306a36Sopenharmony_ci	if (tseg1 > btc->tseg1_max) {
6462306a36Sopenharmony_ci		NL_SET_ERR_MSG_FMT(extack, "prop-seg + phase-seg1: %u greater than tseg1-max: %u",
6562306a36Sopenharmony_ci				   tseg1, btc->tseg1_max);
6662306a36Sopenharmony_ci		return -EINVAL;
6762306a36Sopenharmony_ci	}
6862306a36Sopenharmony_ci	if (bt->phase_seg2 < btc->tseg2_min) {
6962306a36Sopenharmony_ci		NL_SET_ERR_MSG_FMT(extack, "phase-seg2: %u less than tseg2-min: %u",
7062306a36Sopenharmony_ci				   bt->phase_seg2, btc->tseg2_min);
7162306a36Sopenharmony_ci		return -EINVAL;
7262306a36Sopenharmony_ci	}
7362306a36Sopenharmony_ci	if (bt->phase_seg2 > btc->tseg2_max) {
7462306a36Sopenharmony_ci		NL_SET_ERR_MSG_FMT(extack, "phase-seg2: %u greater than tseg2-max: %u",
7562306a36Sopenharmony_ci				   bt->phase_seg2, btc->tseg2_max);
7662306a36Sopenharmony_ci		return -EINVAL;
7762306a36Sopenharmony_ci	}
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	can_sjw_set_default(bt);
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	err = can_sjw_check(dev, bt, btc, extack);
8262306a36Sopenharmony_ci	if (err)
8362306a36Sopenharmony_ci		return err;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	brp64 = (u64)priv->clock.freq * (u64)bt->tq;
8662306a36Sopenharmony_ci	if (btc->brp_inc > 1)
8762306a36Sopenharmony_ci		do_div(brp64, btc->brp_inc);
8862306a36Sopenharmony_ci	brp64 += 500000000UL - 1;
8962306a36Sopenharmony_ci	do_div(brp64, 1000000000UL); /* the practicable BRP */
9062306a36Sopenharmony_ci	if (btc->brp_inc > 1)
9162306a36Sopenharmony_ci		brp64 *= btc->brp_inc;
9262306a36Sopenharmony_ci	bt->brp = (u32)brp64;
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	if (bt->brp < btc->brp_min) {
9562306a36Sopenharmony_ci		NL_SET_ERR_MSG_FMT(extack, "resulting brp: %u less than brp-min: %u",
9662306a36Sopenharmony_ci				   bt->brp, btc->brp_min);
9762306a36Sopenharmony_ci		return -EINVAL;
9862306a36Sopenharmony_ci	}
9962306a36Sopenharmony_ci	if (bt->brp > btc->brp_max) {
10062306a36Sopenharmony_ci		NL_SET_ERR_MSG_FMT(extack, "resulting brp: %u greater than brp-max: %u",
10162306a36Sopenharmony_ci				   bt->brp, btc->brp_max);
10262306a36Sopenharmony_ci		return -EINVAL;
10362306a36Sopenharmony_ci	}
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	bt->bitrate = priv->clock.freq / (bt->brp * can_bit_time(bt));
10662306a36Sopenharmony_ci	bt->sample_point = ((CAN_SYNC_SEG + tseg1) * 1000) / can_bit_time(bt);
10762306a36Sopenharmony_ci	bt->tq = DIV_U64_ROUND_CLOSEST(mul_u32_u32(bt->brp, NSEC_PER_SEC),
10862306a36Sopenharmony_ci				       priv->clock.freq);
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	return 0;
11162306a36Sopenharmony_ci}
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci/* Checks the validity of predefined bitrate settings */
11462306a36Sopenharmony_cistatic int
11562306a36Sopenharmony_cican_validate_bitrate(const struct net_device *dev, const struct can_bittiming *bt,
11662306a36Sopenharmony_ci		     const u32 *bitrate_const,
11762306a36Sopenharmony_ci		     const unsigned int bitrate_const_cnt,
11862306a36Sopenharmony_ci		     struct netlink_ext_ack *extack)
11962306a36Sopenharmony_ci{
12062306a36Sopenharmony_ci	unsigned int i;
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	for (i = 0; i < bitrate_const_cnt; i++) {
12362306a36Sopenharmony_ci		if (bt->bitrate == bitrate_const[i])
12462306a36Sopenharmony_ci			return 0;
12562306a36Sopenharmony_ci	}
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	NL_SET_ERR_MSG_FMT(extack, "bitrate %u bps not supported",
12862306a36Sopenharmony_ci			   bt->brp);
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	return -EINVAL;
13162306a36Sopenharmony_ci}
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ciint can_get_bittiming(const struct net_device *dev, struct can_bittiming *bt,
13462306a36Sopenharmony_ci		      const struct can_bittiming_const *btc,
13562306a36Sopenharmony_ci		      const u32 *bitrate_const,
13662306a36Sopenharmony_ci		      const unsigned int bitrate_const_cnt,
13762306a36Sopenharmony_ci		      struct netlink_ext_ack *extack)
13862306a36Sopenharmony_ci{
13962306a36Sopenharmony_ci	/* Depending on the given can_bittiming parameter structure the CAN
14062306a36Sopenharmony_ci	 * timing parameters are calculated based on the provided bitrate OR
14162306a36Sopenharmony_ci	 * alternatively the CAN timing parameters (tq, prop_seg, etc.) are
14262306a36Sopenharmony_ci	 * provided directly which are then checked and fixed up.
14362306a36Sopenharmony_ci	 */
14462306a36Sopenharmony_ci	if (!bt->tq && bt->bitrate && btc)
14562306a36Sopenharmony_ci		return can_calc_bittiming(dev, bt, btc, extack);
14662306a36Sopenharmony_ci	if (bt->tq && !bt->bitrate && btc)
14762306a36Sopenharmony_ci		return can_fixup_bittiming(dev, bt, btc, extack);
14862306a36Sopenharmony_ci	if (!bt->tq && bt->bitrate && bitrate_const)
14962306a36Sopenharmony_ci		return can_validate_bitrate(dev, bt, bitrate_const,
15062306a36Sopenharmony_ci					    bitrate_const_cnt, extack);
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	return -EINVAL;
15362306a36Sopenharmony_ci}
154