Module: xenomai-forge
Branch: master
Commit: ef036723abcf7e05737c34c099a1efebf69362ae
URL:    
http://git.xenomai.org/?p=xenomai-forge.git;a=commit;h=ef036723abcf7e05737c34c099a1efebf69362ae

Author: Wolfgang Grandegger <w...@denx.de>
Date:   Thu Sep 27 17:29:14 2012 +0200

rtcan: add bit-timing calulation from Linux-CAN

So far we use a very simple method to calculate bit-timing parameters
from the bitrate. This works fine for the SJA1000 and MSCAN but fails
for the Flexcan. This patch introduces the more general bit-timing
algorithm from Linux-CAN, which is based on bit-timing constants.
The Kconfig option XENO_DRIVERS_CAN_CALC_BITTIME_OLD allows to set
the old algorithm for backward compatibility.

Signed-off-by: Wolfgang Grandegger <w...@denx.de>

---

 kernel/drivers/can/Kconfig                 |    6 ++
 kernel/drivers/can/mscan/rtcan_mscan.c     |   17 ++++
 kernel/drivers/can/rtcan_dev.h             |   17 ++++
 kernel/drivers/can/rtcan_raw_dev.c         |  127 +++++++++++++++++++++++++++-
 kernel/drivers/can/sja1000/rtcan_sja1000.c |   18 ++++
 5 files changed, 184 insertions(+), 1 deletions(-)

diff --git a/kernel/drivers/can/Kconfig b/kernel/drivers/can/Kconfig
index ac602ad..f1f31c6 100644
--- a/kernel/drivers/can/Kconfig
+++ b/kernel/drivers/can/Kconfig
@@ -61,6 +61,12 @@ config XENO_DRIVERS_CAN_BUS_ERR
        processing. This option is automatically selected for CAN controllers
        supporting bus error interrupts like the SJA1000.
 
+config XENO_DRIVERS_CAN_CALC_BITTIME_OLD
+       depends on XENO_DRIVERS_CAN
+       bool "Old bit-time calculation algorithm"
+       default n
+       help
+
 config XENO_DRIVERS_CAN_VIRT
        depends on XENO_DRIVERS_CAN
        tristate "Virtual CAN bus driver"
diff --git a/kernel/drivers/can/mscan/rtcan_mscan.c 
b/kernel/drivers/can/mscan/rtcan_mscan.c
index edd590e..2b53e79 100644
--- a/kernel/drivers/can/mscan/rtcan_mscan.c
+++ b/kernel/drivers/can/mscan/rtcan_mscan.c
@@ -42,6 +42,20 @@
 
 #define MSCAN_SET_MODE_RETRIES 255
 
+#ifndef CONFIG_XENO_DRIVERS_CAN_CALC_BITTIME_OLD
+static struct can_bittiming_const mscan_bittiming_const = {
+       .name = "mscan",
+       .tseg1_min = 4,
+       .tseg1_max = 16,
+       .tseg2_min = 2,
+       .tseg2_max = 8,
+       .sjw_max = 4,
+       .brp_min = 1,
+       .brp_max = 64,
+       .brp_inc = 1,
+};
+#endif
+
 /**
  *  Reception Interrupt handler
  *
@@ -722,6 +736,9 @@ int rtcan_mscan_register(struct rtcan_device *dev, int irq, 
int mscan_clksrc)
        dev->hard_start_xmit = rtcan_mscan_start_xmit;
        dev->do_set_mode = rtcan_mscan_set_mode;
        dev->do_set_bit_time = rtcan_mscan_set_bit_time;
+#ifndef CONFIG_XENO_DRIVERS_CAN_CALC_BITTIME_OLD
+       dev->bittiming_const = &mscan_bittiming_const;
+#endif
 
        /* Register IRQ handler and pass device structure as arg */
        ret = rtdm_irq_request(&dev->irq_handle, irq, rtcan_mscan_interrupt,
diff --git a/kernel/drivers/can/rtcan_dev.h b/kernel/drivers/can/rtcan_dev.h
index 4b563b7..a852c30 100644
--- a/kernel/drivers/can/rtcan_dev.h
+++ b/kernel/drivers/can/rtcan_dev.h
@@ -49,6 +49,22 @@
 #define RTCAN_USE_REFCOUNT
 #endif
 
+/*
+ * CAN harware-dependent bit-timing constant
+ *
+ * Used for calculating and checking bit-timing parameters
+ */
+struct can_bittiming_const {
+       char name[16];          /* Name of the CAN controller hardware */
+       __u32 tseg1_min;        /* Time segement 1 = prop_seg + phase_seg1 */
+       __u32 tseg1_max;
+       __u32 tseg2_min;        /* Time segement 2 = phase_seg2 */
+       __u32 tseg2_max;
+       __u32 sjw_max;          /* Synchronisation jump width */
+       __u32 brp_min;          /* Bit-rate prescaler */
+       __u32 brp_max;
+       __u32 brp_inc;
+};
 
 struct rtcan_device {
     unsigned int        version;
@@ -92,6 +108,7 @@ struct rtcan_device {
     can_baudrate_t      baudrate;
 
     struct can_bittime  bit_time;
+    const struct can_bittiming_const *bittiming_const;
 
     /* State which the controller is in. Protected by device_lock in all
      * device structures. */
diff --git a/kernel/drivers/can/rtcan_raw_dev.c 
b/kernel/drivers/can/rtcan_raw_dev.c
index 5e7e260..c5fd1b6 100644
--- a/kernel/drivers/can/rtcan_raw_dev.c
+++ b/kernel/drivers/can/rtcan_raw_dev.c
@@ -28,6 +28,8 @@
 #include "rtcan_raw.h"
 #include "rtcan_internal.h"
 
+#ifdef CONFIG_XENO_DRIVERS_CAN_CALC_BITTIME_OLD
+
 #define RTCAN_MAX_TSEG1  15
 #define RTCAN_MAX_TSEG2  7
 
@@ -108,6 +110,128 @@ static int rtcan_calc_bit_time(struct rtcan_device *dev,
     return 0;
 }
 
+#else /* !CONFIG_XENO_DRIVERS_CAN_CALC_BITTIME_OLD */
+
+/* This is the bit-time calculation method from the Linux kernel */
+
+#define CAN_CALC_MAX_ERROR 50 /* in one-tenth of a percent */
+
+static int can_update_spt(const struct can_bittiming_const *btc,
+                         unsigned int sampl_pt, unsigned int tseg,
+                         unsigned int *tseg1, unsigned int *tseg2)
+{
+    *tseg2 = tseg + 1 - (sampl_pt * (tseg + 1)) / 1000;
+    *tseg2 = clamp(*tseg2, btc->tseg2_min, btc->tseg2_max);
+    *tseg1 = tseg - *tseg2;
+    if (*tseg1 > btc->tseg1_max) {
+       *tseg1 = btc->tseg1_max;
+       *tseg2 = tseg - *tseg1;
+    }
+
+    return 1000 * (tseg + 1 - *tseg2) / (tseg + 1);
+}
+
+static int rtcan_calc_bit_time(struct rtcan_device *dev,
+                              can_baudrate_t bitrate,
+                              struct can_bittime_std *bt)
+{
+    const struct can_bittiming_const *btc = dev->bittiming_const;
+    long rate; /* current bitrate */
+    long rate_error;/* difference between current and target value */
+    long best_rate_error = 1000000000;
+    int spt;   /* current sample point in thousandth */
+    int spt_error;     /* difference between current and target value */
+    int best_spt_error = 1000;
+    int sampl_pt;      /* target sample point */
+    int best_tseg = 0, best_brp = 0;   /* current best values for tseg and brp 
*/
+    unsigned int brp, tsegall, tseg, tseg1, tseg2;
+    u64 v64;
+
+    if (!dev->bittiming_const)
+       return -ENOTSUPP;
+
+    /* Use CIA recommended sample points */
+    if (bitrate > 800000)
+       sampl_pt = 750;
+    else if (bitrate > 500000)
+       sampl_pt = 800;
+    else
+       sampl_pt = 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;
+
+       /* Compute all possible tseg choices (tseg=tseg1+tseg2) */
+       brp = dev->can_sys_clock / (tsegall * bitrate) + tseg % 2;
+
+       /* chose 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 = dev->can_sys_clock / (brp * tsegall);
+       rate_error = abs((long)(bitrate - rate));
+
+       /* tseg brp biterror */
+       if (rate_error > best_rate_error)
+           continue;
+
+       /* reset sample point error if we have a better bitrate */
+       if (rate_error < best_rate_error)
+           best_spt_error = 1000;
+
+       spt = can_update_spt(btc, sampl_pt, tseg / 2, &tseg1, &tseg2);
+       spt_error = abs((long)(sampl_pt - spt));
+       if (spt_error > best_spt_error)
+           continue;
+
+       best_spt_error = spt_error;
+       best_rate_error = rate_error;
+       best_tseg = tseg / 2;
+       best_brp = brp;
+
+       if (rate_error == 0 && spt_error == 0)
+           break;
+    }
+
+    if (best_rate_error) {
+       /* Error in one-tenth of a percent */
+       rate_error = (best_rate_error * 1000) / bitrate;
+       if (rate_error > CAN_CALC_MAX_ERROR) {
+           rtcandev_err(dev,
+                        "bitrate error %ld.%ld%% too high\n",
+                        rate_error / 10, rate_error % 10);
+           return -EDOM;
+       } else {
+           rtcandev_warn(dev, "bitrate error %ld.%ld%%\n",
+                         rate_error / 10, rate_error % 10);
+       }
+    }
+
+    /* real sample point */
+    sampl_pt = can_update_spt(btc, sampl_pt, best_tseg, &tseg1, &tseg2);
+
+    v64 = (u64)best_brp * 1000000000UL;
+    do_div(v64, dev->can_sys_clock);
+    bt->prop_seg = tseg1 / 2;
+    bt->phase_seg1 = tseg1 - bt->prop_seg;
+    bt->phase_seg2 = tseg2;
+    bt->sjw = 1;
+    bt->brp = best_brp;
+
+    /* real bit-rate */
+    rate = dev->can_sys_clock / (bt->brp * (tseg1 + tseg2 + 1));
+
+    rtcandev_dbg(dev, "real bitrate %ld, sampling point %d.%d%%\n",
+                rate, sampl_pt/10, sampl_pt%10);
+
+    return 0;
+}
+
+#endif /* CONFIG_XENO_DRIVERS_CAN_CALC_BITTIME_OLD */
+
 static inline int rtcan_raw_ioctl_dev_get(struct rtcan_device *dev,
                                          int request, struct ifreq *ifr)
 {
@@ -168,7 +292,8 @@ static inline int rtcan_raw_ioctl_dev_set(struct 
rtcan_device *dev,
        if (!dev->do_set_bit_time)
            return 0;
        baudrate = (can_baudrate_t *)&ifr->ifr_ifru;
-       if ((ret = rtcan_calc_bit_time(dev, *baudrate, &bit_time.std)))
+       ret = rtcan_calc_bit_time(dev, *baudrate, &bit_time.std);
+       if (ret)
            break;
        bit_time.type = CAN_BITTIME_STD;
        break;
diff --git a/kernel/drivers/can/sja1000/rtcan_sja1000.c 
b/kernel/drivers/can/sja1000/rtcan_sja1000.c
index 9fd392d..cf91439 100644
--- a/kernel/drivers/can/sja1000/rtcan_sja1000.c
+++ b/kernel/drivers/can/sja1000/rtcan_sja1000.c
@@ -83,6 +83,19 @@ MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION("RT-Socket-CAN driver for SJA1000");
 MODULE_SUPPORTED_DEVICE("SJA1000 CAN controller");
 
+#ifndef CONFIG_XENO_DRIVERS_CAN_CALC_BITTIME_OLD
+static struct can_bittiming_const sja1000_bittiming_const = {
+       .name = "sja1000",
+       .tseg1_min = 1,
+       .tseg1_max = 16,
+       .tseg2_min = 1,
+       .tseg2_max = 8,
+       .sjw_max = 4,
+       .brp_min = 1,
+       .brp_max = 64,
+       .brp_inc = 1,
+};
+#endif
 
 static inline void rtcan_sja_rx_interrupt(struct rtcan_device *dev,
                                          struct rtcan_skb *skb)
@@ -622,6 +635,7 @@ int rtcan_sja_set_bit_time(struct rtcan_device *dev,
        return -EINVAL;
     }
 
+    printk("%s: btr0=%#x btr1=%#x\n", __func__, btr0, btr1);
     chip->write_reg(dev, SJA_BTR0, btr0);
     chip->write_reg(dev, SJA_BTR1, btr1);
 
@@ -757,6 +771,10 @@ int rtcan_sja1000_register(struct rtcan_device *dev)
     dev->do_get_state = rtcan_sja_get_state;
     dev->do_set_bit_time = rtcan_sja_set_bit_time;
     dev->do_enable_bus_err = rtcan_sja_enable_bus_err;
+#ifndef CONFIG_XENO_DRIVERS_CAN_CALC_BITTIME_OLD
+    dev->bittiming_const = &sja1000_bittiming_const;
+#endif
+
     chip->bus_err_on = 1;
 
     ret = rtdm_irq_request(&dev->irq_handle,


_______________________________________________
Xenomai-git mailing list
Xenomai-git@xenomai.org
http://www.xenomai.org/mailman/listinfo/xenomai-git

Reply via email to