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