can: dev: can-calc-bit-timing(): better sample point calculation
authorMarc Kleine-Budde <mkl@pengutronix.de>
Mon, 9 May 2016 12:13:06 +0000 (14:13 +0200)
committerMarc Kleine-Budde <mkl@pengutronix.de>
Fri, 17 Jun 2016 13:39:42 +0000 (15:39 +0200)
This patch optimizes the calculation of the sample point. To understand what it
does have a look at the original implementation.

If there is a combination of timing parameters where both the bitrate and
sample point error are 0 the current implementation will find it.

However if the reference clock doesn't allow an optimal bitrate (this means the
bitrate error is always != 0) there might be several timing parameter
combinations having the same bitrate error. The original implementation will
allways choose the one with the highest brp. The actual sample point error
isn't taken into account.

This patch changes the algorithm to minimize the sample point error, too. Now a
brp/tseg combination is accepted as better if one of these condition are
fulfilled:
1) the bit rate error must be smaller, or
2) the bit rate error must be equal and
   the sample point error must be equal or smaller

If a smaller bit rate error is found the sample point error is reset. This
ensures that we first optimize for small bit rate error and then for small
sample point errors.

Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
drivers/net/can/dev.c

index 910c12e..7188137 100644 (file)
@@ -69,6 +69,7 @@ EXPORT_SYMBOL_GPL(can_len2dlc);
 
 #ifdef CONFIG_CAN_CALC_BITTIMING
 #define CAN_CALC_MAX_ERROR 50 /* in one-tenth of a percent */
+#define CAN_CALC_SYNC_SEG 1
 
 /*
  * Bit-timing calculation derived from:
@@ -83,98 +84,126 @@ EXPORT_SYMBOL_GPL(can_len2dlc);
  * registers of the CAN controller. You can find more information
  * in the header file linux/can/netlink.h.
  */
-static int can_update_spt(const struct can_bittiming_const *btc,
-                         int sampl_pt, int tseg, int *tseg1, int *tseg2)
+static int can_update_sample_point(const struct can_bittiming_const *btc,
+                         unsigned int sample_point_nominal, unsigned int tseg,
+                         unsigned int *tseg1_ptr, unsigned int *tseg2_ptr,
+                         unsigned int *sample_point_error_ptr)
 {
-       *tseg2 = tseg + 1 - (sampl_pt * (tseg + 1)) / 1000;
-       if (*tseg2 < btc->tseg2_min)
-               *tseg2 = btc->tseg2_min;
-       if (*tseg2 > btc->tseg2_max)
-               *tseg2 = btc->tseg2_max;
-       *tseg1 = tseg - *tseg2;
-       if (*tseg1 > btc->tseg1_max) {
-               *tseg1 = btc->tseg1_max;
-               *tseg2 = tseg - *tseg1;
+       unsigned int sample_point_error, best_sample_point_error = UINT_MAX;
+       unsigned int sample_point, best_sample_point = 0;
+       unsigned int tseg1, tseg2;
+       int i;
+
+       for (i = 0; i <= 1; i++) {
+               tseg2 = tseg + CAN_CALC_SYNC_SEG - (sample_point_nominal * (tseg + CAN_CALC_SYNC_SEG)) / 1000 - i;
+               tseg2 = clamp(tseg2, btc->tseg2_min, btc->tseg2_max);
+               tseg1 = tseg - tseg2;
+               if (tseg1 > btc->tseg1_max) {
+                       tseg1 = btc->tseg1_max;
+                       tseg2 = tseg - tseg1;
+               }
+
+               sample_point = 1000 * (tseg + CAN_CALC_SYNC_SEG - tseg2) / (tseg + CAN_CALC_SYNC_SEG);
+               sample_point_error = abs(sample_point_nominal - sample_point);
+
+               if ((sample_point <= sample_point_nominal) && (sample_point_error < best_sample_point_error)) {
+                       best_sample_point = sample_point;
+                       best_sample_point_error = sample_point_error;
+                       *tseg1_ptr = tseg1;
+                       *tseg2_ptr = tseg2;
+               }
        }
-       return 1000 * (tseg + 1 - *tseg2) / (tseg + 1);
+
+       if (sample_point_error_ptr)
+               *sample_point_error_ptr = best_sample_point_error;
+
+       return best_sample_point;
 }
 
 static int can_calc_bittiming(struct net_device *dev, struct can_bittiming *bt,
                              const struct can_bittiming_const *btc)
 {
        struct can_priv *priv = netdev_priv(dev);
-       long best_error = 1000000000, error = 0;
-       int best_tseg = 0, best_brp = 0, brp = 0;
-       int tsegall, tseg = 0, tseg1 = 0, tseg2 = 0;
-       int spt_error = 1000, spt = 0, sampl_pt;
-       long rate;
+       unsigned int bitrate;                   /* current bitrate */
+       unsigned int bitrate_error;             /* difference between current and nominal value */
+       unsigned int best_bitrate_error = UINT_MAX;
+       unsigned int sample_point_error;        /* difference between current and nominal value */
+       unsigned int best_sample_point_error = UINT_MAX;
+       unsigned int sample_point_nominal;      /* nominal sample point */
+       unsigned int best_tseg = 0;             /* current best value for tseg */
+       unsigned int best_brp = 0;              /* current best value for brp */
+       unsigned int brp, tsegall, tseg, tseg1 = 0, tseg2 = 0;
        u64 v64;
 
        /* Use CiA recommended sample points */
        if (bt->sample_point) {
-               sampl_pt = bt->sample_point;
+               sample_point_nominal = bt->sample_point;
        } else {
                if (bt->bitrate > 800000)
-                       sampl_pt = 750;
+                       sample_point_nominal = 750;
                else if (bt->bitrate > 500000)
-                       sampl_pt = 800;
+                       sample_point_nominal = 800;
                else
-                       sampl_pt = 875;
+                       sample_point_nominal = 875;
        }
 
        /* tseg even = round down, odd = round up */
        for (tseg = (btc->tseg1_max + btc->tseg2_max) * 2 + 1;
             tseg >= (btc->tseg1_min + btc->tseg2_min) * 2; tseg--) {
-               tsegall = 1 + tseg / 2;
+               tsegall = CAN_CALC_SYNC_SEG + tseg / 2;
+
                /* Compute all possible tseg choices (tseg=tseg1+tseg2) */
                brp = priv->clock.freq / (tsegall * bt->bitrate) + tseg % 2;
-               /* chose brp step which is possible in system */
+
+               /* choose brp step which is possible in system */
                brp = (brp / btc->brp_inc) * btc->brp_inc;
                if ((brp < btc->brp_min) || (brp > btc->brp_max))
                        continue;
-               rate = priv->clock.freq / (brp * tsegall);
-               error = bt->bitrate - rate;
+
+               bitrate = priv->clock.freq / (brp * tsegall);
+               bitrate_error = abs(bt->bitrate - bitrate);
+
                /* tseg brp biterror */
-               if (error < 0)
-                       error = -error;
-               if (error > best_error)
+               if (bitrate_error > best_bitrate_error)
                        continue;
-               best_error = error;
-               if (error == 0) {
-                       spt = can_update_spt(btc, sampl_pt, tseg / 2,
-                                            &tseg1, &tseg2);
-                       error = sampl_pt - spt;
-                       if (error < 0)
-                               error = -error;
-                       if (error > spt_error)
-                               continue;
-                       spt_error = error;
-               }
+
+               /* reset sample point error if we have a better bitrate */
+               if (bitrate_error < best_bitrate_error)
+                       best_sample_point_error = UINT_MAX;
+
+               can_update_sample_point(btc, sample_point_nominal, tseg / 2, &tseg1, &tseg2, &sample_point_error);
+               if (sample_point_error > best_sample_point_error)
+                       continue;
+
+               best_sample_point_error = sample_point_error;
+               best_bitrate_error = bitrate_error;
                best_tseg = tseg / 2;
                best_brp = brp;
-               if (error == 0)
+
+               if (bitrate_error == 0 && sample_point_error == 0)
                        break;
        }
 
-       if (best_error) {
+       if (best_bitrate_error) {
                /* Error in one-tenth of a percent */
-               error = (best_error * 1000) / bt->bitrate;
-               if (error > CAN_CALC_MAX_ERROR) {
+               v64 = (u64)best_bitrate_error * 1000;
+               do_div(v64, bt->bitrate);
+               bitrate_error = (u32)v64;
+               if (bitrate_error > CAN_CALC_MAX_ERROR) {
                        netdev_err(dev,
-                                  "bitrate error %ld.%ld%% too high\n",
-                                  error / 10, error % 10);
+                                  "bitrate error %d.%d%% too high\n",
+                                  bitrate_error / 10, bitrate_error % 10);
                        return -EDOM;
-               } else {
-                       netdev_warn(dev, "bitrate error %ld.%ld%%\n",
-                                   error / 10, error % 10);
                }
+               netdev_warn(dev, "bitrate error %d.%d%%\n",
+                           bitrate_error / 10, bitrate_error % 10);
        }
 
        /* real sample point */
-       bt->sample_point = can_update_spt(btc, sampl_pt, best_tseg,
-                                         &tseg1, &tseg2);
+       bt->sample_point = can_update_sample_point(btc, sample_point_nominal, best_tseg,
+                                         &tseg1, &tseg2, NULL);
 
-       v64 = (u64)best_brp * 1000000000UL;
+       v64 = (u64)best_brp * 1000 * 1000 * 1000;
        do_div(v64, priv->clock.freq);
        bt->tq = (u32)v64;
        bt->prop_seg = tseg1 / 2;
@@ -182,9 +211,9 @@ static int can_calc_bittiming(struct net_device *dev, struct can_bittiming *bt,
        bt->phase_seg2 = tseg2;
 
        /* check for sjw user settings */
-       if (!bt->sjw || !btc->sjw_max)
+       if (!bt->sjw || !btc->sjw_max) {
                bt->sjw = 1;
-       else {
+       else {
                /* bt->sjw is at least 1 -> sanitize upper bound to sjw_max */
                if (bt->sjw > btc->sjw_max)
                        bt->sjw = btc->sjw_max;
@@ -194,8 +223,9 @@ static int can_calc_bittiming(struct net_device *dev, struct can_bittiming *bt,
        }
 
        bt->brp = best_brp;
-       /* real bit-rate */
-       bt->bitrate = priv->clock.freq / (bt->brp * (tseg1 + tseg2 + 1));
+
+       /* real bitrate */
+       bt->bitrate = priv->clock.freq / (bt->brp * (CAN_CALC_SYNC_SEG + tseg1 + tseg2));
 
        return 0;
 }