linux-rpi
linux-rpi copied to clipboard
mcp25xxfd: invalid IVMIF condition makes the driver stop
I'm constantly getting a situation where the driver stops processing interrupts because it detected an invalid IVMIF situation.
i.e this dmesg message: found IVMIF situation not supported by driver - bdiag = [0x%08x, 0x%08x]
Looking at the registeres I can see that the IMVIF interrupt flag is set even though IMVIE flag is not set and thus the Invalid Message Interrupt is not enabled at all. Also no error flags are set in BDIAG so mcp25xxfd_can_int_handle_ivmif returns an error, making the whole driver stop processing interrupts.
As a workaround I added a check at the beginning of mcp25xxfd_can_int_handle_ivmif to make sure that IMVIE is enabled before proceeding. See the attached diff
This resolves the issue for me however I'm still not sure why I see IVMIF interrupts even though IMVIE is disabled. Any ideas? ivmif.txt
@jlanger : You check it a second time? Because in line 399 there is already this check and you check now again by doubling lines 399/400.
@petmos No the first check is checking for the flag being set, and the new check checks for the interrupt enable bit.
@DanielOgorchock : Oops, I didn't recognized the different last letter. Sorry.
The problem is in the last few lines of mcp25xxfd_can_int_handle_ivmif(). The invalid message interrupt flag (IVMIF) needs to be cleared but the interrupt enable bit (IVMIE) is being used instead.
The problem:
- IVMIF gets set for a valid reason (a valid failure).
- The reason in BDIAG1 is correctly processed by mcp25xxfd_can_int_handle_ivmif_tx() and mcp25xxfd_can_int_handle_ivmif_rx() then cleared for the next IVMIF.
- Not clearing IVMIF means the next iteration of the for loop in mcp25xxfd_can_int() finds another IVMIF condition.
- This causes another processing of BDIAG1 which is all clear and triggers the unsupported IVMIF situation.
Changing the last few lines to this made the problem go away:
/* and clear the interrupt flag until we have received or transmited */
cpriv->status.intf &= ~(MCP25XXFD_CAN_INT_IVMIF);
return mcp25xxfd_cmd_write_mask(spi, MCP25XXFD_CAN_INT, cpriv->status.intf, MCP25XXFD_CAN_INT_IVMIF);