Bus-off recovery with the MSCAN on the MPC5200 is tricky. The hardware
recovers from bus-off automatically if the bus gets idle. Unfortunately
it does not signal the state change from bus-off to error-active via
interrupt. Therefore we stop the MSCAN in case of bus-off to allow the
usual manual and timeout triggered recovery procedure by the software.

Signed-off-by: Wolfram Sang <[email protected]>
Signed-off-by: Wolfgang Grandegger <[email protected]>
---
 kernel/2.6/drivers/net/can/mscan/mscan.c |   41 +++++++++++++++++++++----------
 1 file changed, 28 insertions(+), 13 deletions(-)

Index: trunk/kernel/2.6/drivers/net/can/mscan/mscan.c
===================================================================
--- trunk.orig/kernel/2.6/drivers/net/can/mscan/mscan.c
+++ trunk/kernel/2.6/drivers/net/can/mscan/mscan.c
@@ -237,6 +237,15 @@ static int mscan_start(struct net_device
        return 0;
 }
 
+static void mscan_stop(struct net_device *dev)
+{
+       struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
+
+       out_8(&regs->cantier, 0);
+       out_8(&regs->canrier, 0);
+       mscan_set_mode(dev, MSCAN_INIT_MODE);
+}
+
 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,32)
 static int mscan_start_xmit(struct sk_buff *skb, struct net_device *dev)
 #else
@@ -326,21 +335,20 @@ static netdev_tx_t mscan_start_xmit(stru
        return NETDEV_TX_OK;
 }
 
-static inline int check_set_state(struct net_device *dev, u8 canrflg)
+/* This function returns the old state to see where we came from */
+static enum can_state check_set_state(struct net_device *dev, u8 canrflg)
 {
        struct mscan_priv *priv = netdev_priv(dev);
-       enum can_state state;
-       int ret = 0;
+       enum can_state state, old_state = priv->can.state;
 
-       if (!(canrflg & MSCAN_CSCIF) || priv->can.state > CAN_STATE_BUS_OFF)
-               return 0;
+       if (!(canrflg & MSCAN_CSCIF) || old_state > CAN_STATE_BUS_OFF)
+               return old_state;
 
        state = state_map[max(MSCAN_STATE_RX(canrflg),
                              MSCAN_STATE_TX(canrflg))];
-       if (priv->can.state < state)
-               ret = 1;
+
        priv->can.state = state;
-       return ret;
+       return old_state;
 }
 
 static inline void mscan_get_rx_frame(struct net_device *dev,
@@ -388,8 +396,10 @@ static inline void mscan_get_err_frame(s
        struct net_device_stats *stats = &dev->stats;
 #endif
        struct mscan_priv *priv = netdev_priv(dev);
+       enum can_state old_state;
 
        dev_dbg(ND2D(dev), "error interrupt (canrflg=%#x)\n", canrflg);
+
        frame->can_id = CAN_ERR_FLAG;
 
        if (canrflg & MSCAN_OVRIF) {
@@ -401,7 +411,9 @@ static inline void mscan_get_err_frame(s
                frame->data[1] = 0;
        }
 
-       if (check_set_state(dev, canrflg)) {
+       old_state = check_set_state(dev, canrflg);
+       /* State changed */
+       if (old_state != priv->can.state) {
                switch (priv->can.state) {
                case CAN_STATE_ERROR_WARNING:
                        frame->can_id |= CAN_ERR_CRTL;
@@ -420,6 +432,12 @@ static inline void mscan_get_err_frame(s
                        break;
                case CAN_STATE_BUS_OFF:
                        frame->can_id |= CAN_ERR_BUSOFF;
+                       /*
+                        * The MSCAN on the MPC5200 does recover from bus-off
+                        * automatically. To avoid that we stop the chip.
+                        */
+                       mscan_stop(dev);
+                       priv->can.state = CAN_STATE_BUS_OFF;
                        can_bus_off(dev);
                        break;
                default:
@@ -675,7 +693,6 @@ static int mscan_open(struct net_device 
 
 static int mscan_close(struct net_device *dev)
 {
-       struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
        struct mscan_priv *priv = netdev_priv(dev);
 
        netif_stop_queue(dev);
@@ -683,9 +700,7 @@ static int mscan_close(struct net_device
        napi_disable(&priv->napi);
 #endif
 
-       out_8(&regs->cantier, 0);
-       out_8(&regs->canrier, 0);
-       mscan_set_mode(dev, MSCAN_INIT_MODE);
+       mscan_stop(dev);
        close_candev(dev);
        free_irq(dev->irq, dev);
        priv->open_time = 0;
_______________________________________________
Socketcan-core mailing list
[email protected]
https://lists.berlios.de/mailman/listinfo/socketcan-core

Reply via email to