changeset 3271:4baa86091c2c

* cdl/can_lpc2xxx.cdl: Add CYGHWR_DEVS_CAN_LPC2XXX_BUSOFF_WORKAROUND to enable conditional Bus-Off reset mode workaround. [Bug 1001897] * src/can_lpc2xxx.c: Add function lpc2xxx_reset_error_counters to reset TX and RX error counters to certain values. Function lpc2xxx_start_module now clears both error counters. Add some LPC2XXX_DBG_PRINT statements to lpc2xxx_can_getevent function. Add function lpc2xxx_print_status to ease printing status register content. If a bus error condition occures (ICR_BUS_ERR) the function lpc2xxx_can_getevent now clears the error counters to prevent error ISR from blocking application and to support recovery from bus off condition if option CYGHWR_DEVS_CAN_LPC2XXX_BUSOFF_WORKAROUND is enabled.
author sergeig
date Wed, 18 Sep 2013 16:01:59 +0000
parents e0b2ee45abd2
children a99daf62e693
files packages/devs/can/arm/lpc2xxx/current/ChangeLog packages/devs/can/arm/lpc2xxx/current/cdl/can_lpc2xxx.cdl packages/devs/can/arm/lpc2xxx/current/src/can_lpc2xxx.c
diffstat 3 files changed, 115 insertions(+), 4 deletions(-) [+]
line wrap: on
line diff
--- a/packages/devs/can/arm/lpc2xxx/current/ChangeLog
+++ b/packages/devs/can/arm/lpc2xxx/current/ChangeLog
@@ -1,3 +1,18 @@
+2013-09-17  Uwe Kindler <uwe_kindler@web.de>
+
+	* cdl/can_lpc2xxx.cdl: Add CYGHWR_DEVS_CAN_LPC2XXX_BUSOFF_WORKAROUND
+	to enable conditional Bus-Off reset mode workaround. [Bug 1001897]
+	* src/can_lpc2xxx.c: Add function lpc2xxx_reset_error_counters to
+	reset TX and RX error counters to certain values. Function
+	lpc2xxx_start_module now clears both error counters. Add some
+	LPC2XXX_DBG_PRINT statements to lpc2xxx_can_getevent function. Add
+	function lpc2xxx_print_status to ease printing status register
+	content. If a bus error condition occures (ICR_BUS_ERR) the function
+	lpc2xxx_can_getevent now clears the error counters to prevent error
+	ISR from blocking application and to support recovery from bus off
+	condition if option CYGHWR_DEVS_CAN_LPC2XXX_BUSOFF_WORKAROUND is
+	enabled.
+
 2013-09-11  Uwe Kindler <uwe_kindler@web.de>
 
 	* src/can_accfilt_lpc2xxx.c, src/can_lpc2xxx.c: Fix a compile issue of
--- a/packages/devs/can/arm/lpc2xxx/current/cdl/can_lpc2xxx.cdl
+++ b/packages/devs/can/arm/lpc2xxx/current/cdl/can_lpc2xxx.cdl
@@ -329,6 +329,27 @@ cdl_package CYGPKG_DEVS_CAN_LPC2XXX {
             to the lowest priority 15, allowing a single write to 
             elevate the priority of an individual interrupt."
     }
+
+    # Enable this by default for "LPX22xx" parts only.
+    cdl_option CYGHWR_DEVS_CAN_LPC2XXX_BUSOFF_WORKAROUND {
+        display       "Bus-Off Reset Mode workaround"
+        flavor        bool
+        default_value { CYGHWR_HAL_ARM_LPC2XXX_VARIANT_VERSION == 2 }
+        description   "
+            If the Transmit Error counter contains 255 and another
+            error occurs, the CAN Controller is forced into a state
+            called Bus-Off.  In this state, the following register bits
+            and register values are set: BS (Bus Off Set) in CANxSR, BEI
+            (Bus Error Interrupt) and EI (Error Warning Interrupt) in
+            CANxICR, RM (Reset Mode) in CANxMOD, Transmit Error Counter
+            is set to 127, Receive Error Counter is cleared.
+            On some chips RM (Reset Mode) is not set and RX / TX error
+            counters are not cleared. This may lead to a permanent
+            re-calling of the Bus-Off error ISR which in turn may block
+            the application code from running.  If this workaround is
+            active, the Bus-Off error ISR sets RM (Reset Mode), clears
+            RX / TX error counters."
+    }
     
     cdl_option CYGPKG_DEVS_CAN_LPC2XXX_TESTS {
         display "CAN LPC2xxx device driver tests"
--- a/packages/devs/can/arm/lpc2xxx/current/src/can_lpc2xxx.c
+++ b/packages/devs/can/arm/lpc2xxx/current/src/can_lpc2xxx.c
@@ -1265,14 +1265,45 @@ static Cyg_ErrNo lpc2xxx_enter_lowpower_
 }
 
 
+#ifdef CYGHWR_DEVS_CAN_LPC2XXX_BUSOFF_WORKAROUND
+//===========================================================================
+// Reset error counters to the given values
+//===========================================================================
+static void lpc2xxx_reset_error_counters(lpc2xxx_can_info_t *info,
+    cyg_uint8 rx_errcnt, cyg_uint8 tx_errcnt)
+{
+    lsc_buf_t data;
+    cyg_uint32 regval;
+    HAL_READ_UINT32(CAN_CTRL_MOD(info), regval);
+
+    HAL_WRITE_UINT32(CAN_CTRL_MOD(info), CANMOD_RESET);
+    HAL_READ_UINT32(CAN_CTRL_GSR(info), data.dword);
+    data.bytes[2] = rx_errcnt; // reset RX error counter
+    data.bytes[3] = tx_errcnt; // reset TX error counter to 0
+    HAL_WRITE_UINT32(CAN_CTRL_GSR(info), data.dword);
+
+    HAL_WRITE_UINT32(CAN_CTRL_MOD(info), regval);
+}
+#endif // #ifdef CYGHWR_DEVS_CAN_LPC2XXX_BUSOFF_WORKAROUND
+
+
 //===========================================================================
 // Start CAN module - set CANMOD operational and enable all interrupts
 //===========================================================================
 static void lpc2xxx_start_module(can_channel *chan)
 {
+    lsc_buf_t data;
     cyg_uint32          regval;
     lpc2xxx_can_info_t *info = (lpc2xxx_can_info_t *)chan->dev_priv;
     
+    // before we start CAN module, we clear both error counters to start
+    // with a clean error counter state
+    HAL_WRITE_UINT32(CAN_CTRL_MOD(info), CANMOD_RESET);
+    HAL_READ_UINT32(CAN_CTRL_GSR(info), data.dword);
+    data.bytes[2] = 0; // reset RX error counter to 0
+    data.bytes[3] = 0; // reset TX error counter to 0
+    HAL_WRITE_UINT32(CAN_CTRL_GSR(info), data.dword);
+
     HAL_WRITE_UINT32(CAN_CTRL_MOD(info), CANMOD_OPERATIONAL);  
     //
     // The interrupt enable register is also modified by ISR and DSR so
@@ -1600,6 +1631,27 @@ static bool lpc2xxx_can_putmsg(can_chann
 }
 
 
+#ifdef CYGDBG_DEVS_CAN_LPC2XXX_DEBUG
+//===========================================================================
+// Print status registers
+// This function is only here for debugging purposes to ease printing
+// of status register content
+//===========================================================================
+static void lpc2xxx_print_status(lpc2xxx_can_info_t *info)
+{
+    cyg_uint32 regval;
+    lsc_buf_t data;
+
+    HAL_READ_UINT32(CAN_CTRL_MOD(info), regval);
+    LPC2XXX_DBG_PRINT("CANREG_MOD: 0x%08x\n", regval);
+
+    HAL_READ_UINT32(CAN_CTRL_GSR(info), data.dword);
+    LPC2XXX_DBG_PRINT("RXERR: %d\n", data.bytes[2]);
+    LPC2XXX_DBG_PRINT("TXERR: %d\n", data.bytes[3]);
+}
+#endif
+
+
 //===========================================================================
 // Read event from device driver
 //===========================================================================
@@ -1700,15 +1752,18 @@ static bool lpc2xxx_can_getevent(can_cha
             {
                 pevent->flags |= CYGNUM_CAN_EVENT_WARNING_RX;  
                 info->state = CYGNUM_CAN_STATE_BUS_WARN;
+                LPC2XXX_DBG_PRINT("CYGNUM_CAN_EVENT_WARNING_RX counter (%d) (%p)\n", data.bytes[2], (void*) chan);
             }
             else if (data.bytes[3] >= 96)
             {
                 pevent->flags |= CYGNUM_CAN_EVENT_WARNING_TX;
                 info->state = CYGNUM_CAN_STATE_BUS_WARN;
+                LPC2XXX_DBG_PRINT("CYGNUM_CAN_EVENT_WARNING_TX counter (%d) (%p)\n", data.bytes[3], (void*) chan);
             }
             else
             {
                 info->state = CYGNUM_CAN_STATE_ACTIVE;
+                LPC2XXX_DBG_PRINT("CYGNUM_CAN_STATE_ACTIVE (%p)\n", (void*) chan);
             }
             LPC2XXX_DBG_PRINT("ICR_ERR_WARN (%p)\n", (void*) chan);
         }
@@ -1737,10 +1792,13 @@ static bool lpc2xxx_can_getevent(can_cha
             {
                 pevent->flags |= CYGNUM_CAN_EVENT_ERR_PASSIVE;    
                 info->state = CYGNUM_CAN_STATE_ERR_PASSIVE;
+                LPC2XXX_DBG_PRINT("CYGNUM_CAN_EVENT_ERR_PASSIVE (%p)\n", (void*) chan);
             }
             else
             {
+                pevent->flags |= CYGNUM_CAN_EVENT_ERR_ACTIVE;
                 info->state = CYGNUM_CAN_STATE_ACTIVE;    
+                LPC2XXX_DBG_PRINT("CYGNUM_CAN_EVENT_ERR_ACTIVE (%p)\n", (void*) chan);
             }
             LPC2XXX_DBG_PRINT("ICR_ERR_PASSIVE (%p)\n", (void*) chan);
         }
@@ -1758,13 +1816,30 @@ static bool lpc2xxx_can_getevent(can_cha
 #endif // CYGOPT_DEVS_CAN_LPC2XXX_ALIE
         
         //
-        // 1: Bus Error Interrupt -- this bit is set if the BEIE bit in CANIE is 1, and the CAN
-        // controller detects an error on the bus.
+        // 1: Bus Error Interrupt -- this bit is set if the BEIE bit in CANIE
+        // is 1, and the CAN controller detects an error on the bus.
         //
         if (event & ICR_BUS_ERR)
         {
             pevent->flags |= CYGNUM_CAN_EVENT_BUS_OFF; 
             LPC2XXX_DBG_PRINT("ICR_BUS_ERR (%p)\n", (void*) chan);
+
+#ifdef CYGHWR_DEVS_CAN_LPC2XXX_BUSOFF_WORKAROUND
+            //
+            // we do exactly here, what is written in the user manual
+            // 1. we go into reset mode
+            // 2. we clear the RX error counter and set the TX error counter
+            //    to 127
+            // 3. we clear the reset mode
+            // This ensures, that this ISR does not fire again and again and
+            // blocks the application while the bus off condition is active.
+            // Setting the TX error counter to 127 ensures that the controller
+            // is in TX error passive mode and that it does not flood the CAN
+            // bus with error messages. This makes it possible for the
+            // controller to properly recover from a bus off condition
+            //
+            lpc2xxx_reset_error_counters(info, 0, 127);
+#endif
         }
         
 #ifdef CYGOPT_DEVS_CAN_LPC2XXX_LUT_ERR_SUPP
@@ -1851,7 +1926,7 @@ static void lpc2xxx_can_tx_DSR(cyg_vecto
 {
     can_channel              *chan = (can_channel *)data;
     cyg_uint32                regval;
-    lpc2xxx_can_info_t       *info = (lpc2xxx_can_info_t *)chan->dev_priv;
+    CAN_DECLARE_INFO(chan);
     
     //
     // First read the ICR register to acknowledge all interrupts and
@@ -2025,8 +2100,8 @@ static void lpc2xxx_can_DSR(cyg_vector_t
     // Loop through all channels - we need to do this only if we have more
     // than one channel so we can optimize here for single channel
     //
+    cyg_uint8 i = 0;
 #if CYGINT_IO_CAN_CHANNELS > 1 
-    cyg_uint8 i = 0;     
     while (lpc2xxx_global_can_info.active_channels[i])
 #endif
     {