View Issue Details

IDProjectCategoryView StatusLast Update
0001274fss5pv210_LinuxCANpublic2014-01-28 11:44
ReporterKeller Assigned To 
PrioritynormalSeverityminorReproducibilityalways
Status resolvedResolutionfixed 
Product VersionarmStoneA8-V1.1 
Target Versionfss5pv210-V2.0Fixed in Versionfss5pv210-V2.0 
Summary0001274: Accessing CAN locks up the system
DescriptionIf a CAN message is sent or received, the whole system freezes.
Forum Link

Activities

Keller

2012-08-24 19:46

manager   ~0000781

The MCP2515 uses a low-level interrupt line. So basically the
IRQ used on the driver should also be low-level triggered.

The mcp251x driver uses an interrupt service thread (IST) to
handle the CAN interrupt. In addition to the thread, there
should also be an interrupt handler that removes the interrupt
source and then returns IRQ_WAKE_THREAD to delay the main work
to the IST. However removing the interrupt source would mean in
our case masking the interrupt of the GPIO pin, because
removing the real source in the CAN controller would involve
an SPI cycle that can not be done in hard interrupt context.

But there is no infrustructure available in the kernel to just
mask the interrupt. We can only call disable_irq() to do this.
However the IST will verify this and won't do its job then.

So if we have a low-level interrupt and we switch off the IRQ
in the handler, we don't get our interrupt handled in the IST.
And if we keep the IRQ running (as does the default handler
in the kernel if we don't provide our own handler in the driver),
we are stuck in an endless loop because the handler is called
forever if the source is not removed.

The standard driver solves this dilemma by using a falling-edge
triggered interrupt. Then we're not stuck in an endless loop.
This is the solution that we also have implemented now. However
I'm not sure if this can cause a race condition where we miss
an interrupt if a second falling edge is detected while we're
still in the IST. Then the handler is called again but if this
really calls the IST again after it returns from the current
task I can not tell.

Another idea is to switch the IRQ to falling-edge while handling
the IST to avoid the endless loop, and later switch it back to
low-level if the IST returns. But this will trigger the handler
always twice, because a low-level interrupt always keeps the
interrupt pending flag set. So the handler is called with
low-level interrupt, the pending flag is already set again,
the handler sets the interrupt to falling edge and returns.
Then it is called once again because of the pending flag and
returns a second time. Now no new interrupt is pending and the
IST can do its work. Because of this two-times calling, this is
also no perfect solution.

The best thing would be to have a function to mask a GPIO
interrupt without officially disabling it at the same time.
There exist the functions mask_irq()/unmask_irq() in
kernel/irq/chip.c but at a first glance they can not be called
from a driver. But maybe we can check this again if the
version with falling-edge interrupt is not working reliably.