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(®s->cantier, 0); + out_8(®s->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(®s->cantier, 0); - out_8(®s->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
