CAN MCP2515 GPIO IRQ problem

I'm trying to setup a MCP2515 CAN controller on the second SPI channel on an Onion Omega2 using the actual OpenWRT master (Kernel 5.4) . The SPI part is fine (half duplex mcp251x patch will be mainlined soon) as the controller initialization works:

root@Omega2:~# insmod mcp251x
[44967.908881] mcp251x spi0.1 can0: MCP2515 successfully initialized.

but I'm struggeling with the GPIO IRQ part. I want to use GPIO16 for the MCP2515 interrupt.The DTS part looks like (according to the mcp251x devictree binding docs):

#
/ {
        compatible = "onion,omega2", "mediatek,mt7628an-soc";
       ...
};

&spi0 {
        status = "okay";

        pinctrl-names = "default";
        pinctrl-0 = <&spi_pins>, <&spi_cs1_pins>;

        flash@0 {
                regs = <0>;
                ...
        };
        can0: can@1 {
                compatible = "microchip,mcp2515";
                reg = <1>;
                clock-frequency = <8000000>;
                interrupt-parent = <&gpio>;
                interrupts = <16 0x8>;
                spi-max-frequency = <10000000>;
        };
};

Setting up the CAN interface (ip link set can0 up type can bitrate 250000 loopback on) runs into following Ooops:

[45798.219321] irq 41: nobody cared (try booting with the "irqpoll" option)
[45798.226135] CPU: 0 PID: 3094 Comm: ip Not tainted 5.4.108 #0
[45798.231866] Stack : 80580000 8052bba8 00000000 00000000 8052ad88 83c0bca4 83d62c80 80562c63
[45798.240344]         804d9398 00000c16 806b33bc 8056d6f0 804d333c 00000001 83c0bc58 b5de4858
[45798.248818]         00000000 00000000 806e0000 00000000 00000030 000000e2 746f4e20 69617420
[45798.257291]         00000000 000008e6 00000000 00037357 80000000 00000000 00000029 83c0be4c
[45798.265763]         0000000e 8056d6f0 804d333c 80570000 00000003 8026c64c 00000000 806b0000
[45798.274237]         ...
[45798.276714] Call Trace:
[45798.279218] [<80009b70>] show_stack+0x30/0x100
[45798.283734] [<80058ab8>] __report_bad_irq+0x58/0xf8
[45798.288680] [<80058e8c>] note_interrupt+0x2c4/0x314
[45798.293625] [<80056238>] handle_irq_event_percpu+0x60/0x78
[45798.299185] [<8005628c>] handle_irq_event+0x3c/0x64
[45798.304131] [<8005954c>] handle_simple_irq+0x94/0xbc
[45798.309164] [<80055950>] generic_handle_irq+0x38/0x50
[45798.314305] [<8022bb88>] mediatek_gpio_irq_handler+0x74/0xf0
[45798.320042] [<80056098>] __handle_irq_event_percpu+0x78/0x1b8
[45798.325867] [<800561f8>] handle_irq_event_percpu+0x20/0x78
[45798.331425] [<8005628c>] handle_irq_event+0x3c/0x64
[45798.336371] [<80059a18>] handle_level_irq+0xc8/0x164
[45798.341403] [<80055950>] generic_handle_irq+0x38/0x50
[45798.346524] [<80055950>] generic_handle_irq+0x38/0x50
[45798.351663] [<80459f1c>] do_IRQ+0x1c/0x2c
[45798.355729] [<80005670>] except_vec_vi_end+0xb8/0xc4
[45798.360764] [<80005670>] except_vec_vi_end+0xb8/0xc4
[45798.365793] handlers:
[45798.368102] [<b274c36f>] irq_default_primary_handler threaded [<148b743b>] 0x81d11110
[45798.376045] Disabling IRQ #41

So there is now interaction between mcp251x driver and the GPIO IRQ

Some more informations:

root@Omega2:~# cat /proc/interrupts  ; ./io.sh 
           CPU0       
  5:      71612      MIPS   5  10100000.ethernet
  6:         21      MIPS   6  mt7603e
  7:   11852071      MIPS   7  timer
 14:     100001      INTC   6  10000600.gpio-bank0, 10000600.gpio-bank1, 10000600.gpio-bank2
 22:          0      INTC  14  10130000.sdhci
 25:          5      INTC  17  esw
 26:          1      INTC  18  ehci_hcd:usb1, ohci_hcd:usb2
 28:        504      INTC  20  ttyS0
 40:          0  10000600.gpio   6  keys
 41:     100001  10000600.gpio  16  spi0.1
ERR:          0

MT7688 GPIO IRQ regs:
10000650:  00000000 00000040 00000000 ffffffff
10000660:  00000000 00000040 00000000 ffffffff
10000670:  00000000 00000000 00000000 ffffffff
10000680:  00000000 00000000 00000000 ffffffff
10000690:  00000000 00000000 00000000 ffffffff
100006a0:  00000000 00000000 00000000 ffffffff

Does anybody have a hint howto fix it ?

The problem is still present. I'm using the 21.02 branch and made some tests.
Using the GPIO 16 with IRQ on Falling Edge:
interrupts = <16 0x02>
the IRQ appears on the right position but on wrong GPIO bank:

           GPIO
           0-31     32-63    64-95
10000650:  00000000 00000040 00000000 ffffffff GPIO IRQ Risng Edge
10000660:  00000000 00000040 00010000 ffffffff GPIO IRQ Falling Edge
10000670:  00000000 00000000 00000000 ffffffff GPIO IRQ Level High
10000680:  00000000 00000000 00000000 ffffffff GPIO IRQ Level Low
10000690:  00000000 00000000 00000000 ffffffff GPIO Interrupt Status
100006a0:  00000000 00000000 00000000 ffffffff GPIO Edge Status

The interrupt appears on GPIO 16 on the third bank (0x00010000).
I tried GPIO 15 and the value changed to 0x00008000. So it's all
about the GPIO bank.

It looks like that the problem is located in gpio-mt7621.c, where the GPIO
banks are not corresponding correctly with device tree settings.

To be honest I'm not very familiar with device tree bindings to the kernel.
Does anybody have a hint how I could fix it ?

@GBert did you ever figure this out? I'm running into the same error.

No, I didn't figured it out. There were some discussion in the Onion Community:

but I did not test the mentioned patches.
It looks like it's only a simple fault but where ?

1 Like

No luck unfortunately. The patch didn't change anything for me. Thanks for pointing me towards that though.

Okay, I've made some progress on this, OpenWRT version 22.03.

I'm able to run ip link set can0 up type can bitrate 250000, without it crashing now.

I had to modify the gpio-mt7621.c on line 275 from:
girq->handle = handle_simple_irq;
to:
girq->handle = handle_level_irq;

I also had to modify the mcp251x.c file starting on line 1229 from:

if(!dev_fwnode(&spi->dev)){ 
    flags = IRQF_TRIGGER_FALLING;
} 

to:

flags = IRQF_ONESHOT | IRQF_TRIGGER_LOW; 

It's hanging now on line 759 where it disables the IRQ to initialize the MCP chip.
If you comment out this line it does successfully start the MCP chip and continue on into the rx/tx loop, HOWEVER it starts throttling since the irq is using 90% of the cpu.

I'm still investigating but I'm getting close I think.

Without any changes I was also able to setup the CAN interface. Until you don't send or receive any CAN frame anything looks fine. The problem begins, when the CAN controller generates the first interrupt (send or receive packet).
To investigate the problem I set the IRQ to falling edge, which isn't correct. It's need to be set to Level (low) for a fully working mcp251x module.

I know it's an old post, but have you guys figure a workaround. I'm facing very similar problem with no hope so far.

I got it working eventually, but it ended up being a mix of a couple different things. I ended up starting on 19.07 instead of the latest.

I only had to modify two files, if you don't count the kernel config, and the DTS file.

gpio-mt7621.c

  • Added LEVEL_LOW and LEVEL_HIGH triggers

mcp251x.c

  • Changed the flags from IRQF_ONESHOT | IRQF_TRIGGER_FALLING; to IRQF_ONESHOT | IRQF_TRIGGER_LOW
  • Added support for half duplex spi instead of full duplex, since this version of the mcp driver in the kernel didn't have it.
  • called gpio_request_one to set the interrupt since reading from the dts file didn't seem to work
  • explicitly set the freq due to the same issue as above.

I'm not honestly sure why reading the set values in the device tree didn't work but that was the last thing I needed before the driver started working.

Thank you very much for your response and most importantly for the glimmer of hope that you gave me after laterally pulling my hear.

Can you please share those two files as I'm not very familiar with how adding trigger works as well as adding support for half duplex SPI. It seems the driver version I'm using already supports half-duplex I added some printk during initialization to confirm that (I'm on 23.05):

My two files look exactly like these below, which lines needs to be changed?:

My .dts:

&spi0 {
        status = "okay";

        flash@0 {
                compatible = "jedec,spi-nor";
                reg = <0>;
               ...
        };

        can0: can@1 {
                compatible = "microchip,mcp2515";
                reg = <1>;
                clock-frequency = <16000000>;
                spi-max-frequency = <10000000>;
                interrupt-parent = <&gpio>;
                interrupts = <11 0x8>;
                status = "okay";
        };
};

Also, is there any specific pre-requirement modules to be enabled in kernal config such as spi-dev or any other? Thank you very much

You're on a different version of OpenWRT than I am, but what finally got everything working for me was directly requesting the GPIO as an interrupt using the gpio_request_one function.

Sure, let me dig into the code, from your device tree did you end up using

interrupts = <16 0x8>;
or 
interrupts = <16 0x2>;

This is what I ended up with, I'm not sure how much it matters since I ended up hardcoding values in the driver.

can0: mcp251x@1{
	#address-cells=<1>;
	#size-cells=<1>;
	reg=<1>;
	compatible="microchip,mcp2515";
	spi-max-frequency=<2000000>;
	interrupt-parent=<&gpio0>;
	interrupts=<3 8>;
};

it looks like i'm almost there can you share your gpio_request_one function call and what is the best place to added in the driver?

I added it to the can_probe function since that's where everything gets setup initally.

static int mcp251x_can_probe(struct spi_device *spi)
{
...
ret = gpio_request_one(GPIO_INT, GPIOF_IN | GPIOF_EXPORT_DIR_FIXED, "mcp251x:int");
	if (ret < 0)
		goto out_msg;

	ret = gpio_to_irq(GPIO_INT);
	if (ret < 0)
		goto out_no_free;
	spi->irq = ret;
...

I believe I got the mcp251x_can_ist to kick in but it's getting stuck/hanging on mutex_lock:
image

I saw that you experienced hanging issue. Was that due mutex_lock?

Also, did you end up changing the below? when I change
girq->handle = handle_simple_irq;
to:
girq->handle = handle_level_irq;

In my case if I change it to girq->handle = handle_level_irq and comment the mutex_lock my mcp251x_can_ist function keep triggering.

When I put the original girq->handle = handle_simple_irq; back i get the very first error
[45798.219321] irq 41: nobody cared (try booting with the "irqpoll" option)

I know that I'm close but not sure where is the problem.

I got it to work. It was my mistake all along as I was setting my interrupt on the wrong gpio I also had to change girq->handle = handle_simple_irq to girq->handle = handle_level_irq. Thank you very much

1 Like

Can you share your final changes? I'd like to update to the latest openwrt version if possible and that nobody cares message was the last thing that was hanging me up.

Two Changes Only:

  1. in drivers/gpio/gpio-mt7621.c change the following:

    girq->handler = handle_simple_irq;
    to
    girq->handler = handle_level_irq;
    
  2. in mcp251x.c I added the below changes:

    #define GPIO_INT                491
    
    ....
     /* in mcp251x_open function i forced the flags as below */
     flags = IRQF_TRIGGER_LOW | IRQF_ONESHOT;
    
      /* in mcp251x_can_probe function I added the below */
      ret = gpio_request_one(GPIO_INT, GPIOF_IN, "mcp2515_gpio");
      if (ret) {
              printk("gpio_request_one: %d", ret);
              goto error_probe;
      }
      ret = gpio_to_irq(GPIO_INT);
      if (ret < 0) {
              printk("gpio_to_irq: %d", ret);
              goto error_probe;
      }
      spi->irq = ret;
    

Just in case you need it my .dts file looks like this:

        can0: mcp2515@1 {
                #address-cells = <1>;
                #size-cells = <1>;
                compatible = "microchip,mcp2515";
                reg = <1>;
                clock-frequency = <16000000>;  // 16 MHz
                spi-max-frequency = <10000000>;
                interrupt-parent = <&gpio>;
                interrupts = <11 IRQ_TYPE_LEVEL_LOW>;
                status = "okay";
        };