I'm developing a kernel module that needs to handle interrupts from 4 GPIOs, using an IO expander (MCP23008). I am working on a Raspberry Pi 3B+ with kernel 5.10.1-v7 (buildroot image). The module (mymodule) works as intended when using the "normal" GPIOs from the 40 pin header. However, when I try to use GPIOs from the pinctrl-mcp23s08 module, the request_interrupt function fails and returns -22 (EINVAL).
What am I missing? It seems to me everything is in place to request an interrupt from the expanded GPIOs. Other similar threads point to the IRQF_SHARED flag being used with NULL in the 5th argument of request_interrupt, but that is not my case.
Here's the relevant device-tree snippets:
/* for the io expander (mcp23s08) module */
&i2c1 { compatible = "brcm,bcm2835-i2c";
pinctrl-names = "default";
pinctrl-0 = <&i2c1_pins>;
clock-frequency = <100000>;
status="okay";
mcp23008: mcp23008@20 {
compatible = "microchip,mcp23008";
reg=<0x20>; /* i2c slave address */
gpio-controller; /* mark the node as gpio-controller */
#gpio-cells=<2>; /* pin number and flags */
interrupt-parent = <&gpio>;
interrupt-controller; /* mark the node as interrupt-controller */
#interrupt-cells=<2>; /* pin number and flags */
interrupts = <16 IRQ_TYPE_LEVEL_LOW>;
status="okay";
};
};
/* for mymodule */
/ {
compatible = "raspberrypi,3-model-b-plus", "brcm,bcm2837";
model = "Raspberry Pi 3 Model B+";
mymodule {
compatible = "myvendor,mymodule";
gpios = <&mcp23008 0 GPIO_ACTIVE_HIGH>,
<&mcp23008 1 GPIO_ACTIVE_HIGH>,
<&mcp23008 2 GPIO_ACTIVE_HIGH>,
<&mcp23008 3 GPIO_ACTIVE_HIGH>;
interrupt-parent = <&mcp23008>;
interrupts = <0 IRQ_TYPE_LEVEL_HIGH>,
<1 IRQ_TYPE_LEVEL_HIGH>,
<2 IRQ_TYPE_LEVEL_HIGH>,
<3 IRQ_TYPE_LEVEL_HIGH>;
status = "okay";
};
};
I know the mcp23s08 module is inserted correctly at boot, since I can export GPIOs from sysfs and read their values. Also, in the init function of mymodule, the gpio_request_array function succeeds with the GPIOs given by the mcp23s08 module (gpio 496 to 499). Finally this line in /proc/interrupts tells me that the interrupt for the mcp23s08 driver (on gpio 16) is correctly assigned:
$ cat /proc/interrupts
CPU0 CPU1 CPU2 CPU3
50: 5505 0 0 0 ARMCTRL-level 1 Edge 3f00b880.mailbox
51: 47 0 0 0 ARMCTRL-level 2 Edge VCHIQ doorbell
73: 0 0 0 0 ARMCTRL-level 48 Edge bcm2708_fb DMA
75: 12435 0 0 0 ARMCTRL-level 50 Edge DMA IRQ
77: 716 0 0 0 ARMCTRL-level 52 Edge DMA IRQ
78: 16 0 0 0 ARMCTRL-level 53 Edge DMA IRQ
81: 0 0 0 0 ARMCTRL-level 56 Edge DMA IRQ
86: 199 0 0 0 ARMCTRL-level 61 Edge ttyS0
89: 887966 0 0 0 ARMCTRL-level 64 Edge dwc_otg, dwc_otg_pcd, dwc_otg_hcd:usb1
110: 2 0 0 0 ARMCTRL-level 85 Edge 3f804000.i2c
111: 0 0 0 0 ARMCTRL-level 86 Edge 3f204000.spi
113: 119 0 0 0 ARMCTRL-level 88 Edge mmc0
119: 214231 0 0 0 ARMCTRL-level 94 Edge mmc1
194: 0 0 0 0 bcm2836-timer 0 Edge arch_timer
195: 91376 256895 932964 45993 bcm2836-timer 1 Edge arch_timer
198: 0 0 0 0 bcm2836-pmu 9 Edge arm-pmu
199: 1 0 0 0 pinctrl-bcm2835 4 Edge pps.-1
200: 0 0 0 0 pinctrl-bcm2835 16 Level 1-0020
205: 0 0 0 0 lan78xx-irqs 17 Edge usb-001:004:01
FIQ: usb_fiq
IPI0: 0 0 0 0 CPU wakeup interrupts
IPI1: 0 0 0 0 Timer broadcast interrupts
IPI2: 91 73 100 88 Rescheduling interrupts
IPI3: 4671 20748 12983 31858 Function call interrupts
IPI4: 0 0 0 0 CPU stop interrupts
IPI5: 0 0 0 0 IRQ work interrupts
IPI6: 0 0 0 0 completion interrupts
Err: 0
Here's the relevant init code that fails to request the interrupt:
static int __init
mymodule_init(void)
{
int i;
int err;
/* [...] */
err = platform_driver_register(&gpio_driver);
if (err) {
printk(KERN_ERR "mymodule: failed to register platform driver\n");
goto fail_platform_driver_register;
}
err = gpio_request_array(gpios, COUNT_OF(gpios));
if (err != 0) {
printk(KERN_ERR "mymodule: failed to request gpio array: %d\n", err);
goto fail_gpio_request_array;
}
for (i = 0; i < COUNT_OF(irq_ids); ++i) {
err = platform_get_irq(dev.platform_device, i);
if (err > 0) {
irq_ids[i] = err;
printk(KERN_INFO "i=%d irq=%d\n", i, irq_ids[i]);
} else {
printk(KERN_ERR "mymodule: gpio_to_irq failed i = %d\n", i);
goto fail_platform_get_irq;
}
}
for (i = 0; i < COUNT_OF(irq_ids); ++i) {
err = request_irq(
irq_ids[i],
(irq_handler_t) mymodule_irq_handler,
IRQF_TRIGGER_HIGH,
"mymodule_irq_handler",
NULL
);
if (err) {
printk(KERN_ERR "mymodule: failed to request_irq at i=%d irq=%d: %d\n",
i, irq_ids[i], err);
goto fail_request_irq;
}
}
/* success, return here */
return 0;
/* ommited: error handling here */
}
Finally, here's the dmesg output:
[ 76.548689] i=0 irq=201
[ 76.554473] i=1 irq=202
[ 76.560189] i=2 irq=203
[ 76.566033] i=3 irq=204
[ 76.568525] mymodule: failed to request_irq at i=0 irq=201: -22