summaryrefslogblamecommitdiffstats
path: root/bsps/shared/grlib/can/canbtrs.c
blob: 307547475c2980e79afc85f3d8a5fed603d2b7f7 (plain) (tree)















































































































































                                                                                                                  
/* SPDX-License-Identifier: BSD-2-Clause */

/**
 * @file
 *
 * @ingroup can
 *
 * @brief Common CAN baud-rate routines for OCCAN/GRCAN/GRCANFD controllers
 *
 * Implements common routines for calculating CAN baud-rate parameters from
 * a user provided baud-rate speed.
 */

/*
 * Copyright (C) 2019, 2020 Cobham Gailer AB
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include <grlib/canbtrs.h>

/*#define GRLIB_CANBTRS_DEBUG*/

/* Calculate CAN baud-rate generation parameters from requested baud-rate */
int grlib_canbtrs_calc_timing(
	unsigned int baud,
	unsigned int core_hz,
	unsigned int sampl_pt,
	struct grlib_canbtrs_ranges *br,
	struct grlib_canbtrs_timing *timing
	)
{
	int best_error = 2000000000, best_tseg=0, best_scaler=0;
	int tseg=0, tseg1=0, tseg2=0, sc, tmp, error;

	/* Default to 80% sample point */
	if ((sampl_pt < 50) || (sampl_pt > 99))
		sampl_pt = 80;

	/* step though all TSEG1+TSEG2 values possible */
	for (tseg = (br->min_tseg1 + br->min_tseg2);
	     tseg <= (br->max_tseg1 + br->max_tseg2);
	     tseg++) {
		/* calculate scaler */
		tmp = ((br->divfactor + tseg) * baud);
		sc = (core_hz * 2)/ tmp - core_hz / tmp;
		if (sc <= 0 || sc > br->max_scaler)
			continue;
		if (br->has_bpr &&
		    (((sc > 256 * 1) && (sc <= 256 * 2) && (sc & 0x1)) ||
		    ((sc > 256 * 2) && (sc <= 256 * 4) && (sc & 0x3)) ||
		    ((sc > 256 * 4) && (sc <= 256 * 8) && (sc & 0x7))))
			continue;

		error = baud - core_hz / (sc * (br->divfactor + tseg));
#ifdef GRLIB_CANBTRS_DEBUG
		printf("  baud=%d, tseg=%d, sc=%d, error=%d\n",
		       baud, tseg, sc, error);
#endif
		if (error < 0)
			error = -error;

		/* tseg is increasing, so we accept higher tseg with the same
		 * baudrate to get better sampling point.
		 */
		if (error <= best_error) {
			best_error = error;
			best_tseg = tseg;
			best_scaler = sc;
#ifdef GRLIB_CANBTRS_DEBUG
			printf("  ! best baud=%d\n",
			       core_hz/(sc * (br->divfactor + tseg)));
#endif
		}
	}

	/* return an error if 5% off baud-rate */
	if (best_error && (baud / best_error <= 5)) {
		return -2;
	} else if (!timing) {
		return 0; /* nothing to store result in, but a valid bitrate can be calculated */
	}

	tseg2 = (best_tseg + br->divfactor) -
	        ((sampl_pt * (best_tseg + br->divfactor)) / 100);
	if (tseg2 < br->min_tseg2) {
		tseg2 = br->min_tseg2;
	} else if (tseg2 > br->max_tseg2) {
		tseg2 = br->max_tseg2;
	}

	tseg1 = best_tseg - tseg2;
	if (tseg1 > br->max_tseg1) {
		tseg1 = br->max_tseg1;
		tseg2 = best_tseg - tseg1;
	} else if (tseg1 < br->min_tseg1) {
		tseg1 = br->min_tseg1;
		tseg2 = best_tseg - tseg1;
	}

	/* Get scaler and BPR from pseudo SCALER clock */
	if (best_scaler <= 256) {
		timing->scaler = best_scaler - 1;
		timing->bpr = 0;
	} else if (best_scaler <= 256 * 2) {
		timing->scaler = ((best_scaler + 1) >> 1) - 1;
		timing->bpr = 1;
	} else if (best_scaler <= 256 * 4) {
		timing->scaler = ((best_scaler + 1) >> 2) - 1;
		timing->bpr = 2;
	} else {
		timing->scaler = ((best_scaler + 1) >> 3) - 1;
		timing->bpr = 3;
	}

	timing->ps1    = tseg1;
	timing->ps2    = tseg2;
	timing->rsj    = 1;

#ifdef GRLIB_CANBTRS_DEBUG
	printf("  ! result: sc=%d,bpr=%d,ps1=%d,ps2=%d\n", timing->scaler, timing->bpr, timing->ps1, timing->ps2);
#endif

	return 0;
}