From ef1d2e286a2d8876e03a3f58ea1a1f549727e518 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 11 Apr 2014 08:13:11 +0000 Subject: [PATCH] can: c_can: Make bus off interrupt disable logic work The state change handler is called with device interrupts disabled already. So no point in disabling them again when we enter bus off state. But what's worse is that we reenable the interrupts at the end of NAPI poll unconditionally. So c_can_start() which is called from the restart timer can trigger interrupts which confuse the hell out of the half reinitialized driver/hw. Remove the pointless device interrupt disable in the BUS_OFF handler and prevent reenabling the device interrupts at the end of the poll routine when the current state is BUS_OFF. Signed-off-by: Thomas Gleixner Tested-by: Alexander Stein Signed-off-by: Marc Kleine-Budde --- drivers/net/can/c_can/c_can.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/drivers/net/can/c_can/c_can.c b/drivers/net/can/c_can/c_can.c index b1629a47c03b..8c725f40bc1d 100644 --- a/drivers/net/can/c_can/c_can.c +++ b/drivers/net/can/c_can/c_can.c @@ -954,11 +954,6 @@ static int c_can_handle_state_change(struct net_device *dev, /* bus-off state */ priv->can.state = CAN_STATE_BUS_OFF; cf->can_id |= CAN_ERR_BUSOFF; - /* - * disable all interrupts in bus-off mode to ensure that - * the CPU is not hogged down - */ - c_can_enable_all_interrupts(priv, DISABLE_ALL_INTERRUPTS); can_bus_off(dev); break; default: @@ -1089,6 +1084,7 @@ static int c_can_poll(struct napi_struct *napi, int quota) netdev_dbg(dev, "entered bus off state\n"); work_done += c_can_handle_state_change(dev, C_CAN_BUS_OFF); + goto end; } /* handle bus recovery events */ @@ -1122,8 +1118,9 @@ static int c_can_poll(struct napi_struct *napi, int quota) end: if (work_done < quota) { napi_complete(napi); - /* enable all IRQs */ - c_can_enable_all_interrupts(priv, ENABLE_ALL_INTERRUPTS); + /* enable all IRQs if we are not in bus off state */ + if (priv->can.state != CAN_STATE_BUS_OFF) + c_can_enable_all_interrupts(priv, ENABLE_ALL_INTERRUPTS); } return work_done;