The example presented here demonstrates how to configure an interrupt-based GPIO enput within the Linux kernel driver for FMQL45T900, which shares similarities with Zynq-7000 platforms.
To begin, determine the correct GPIO number corresponding to the physical pin. For instance, EMIO5 maps to GPIO number 431 on the FMQL45T900 platform. In Vivado, enable EMIO configuration, expand it to 64 bits, and export the required pins externally by generating the top-level wrapper.
Below is the device tree reference and macro definitions used:
/*
Test Pin Configuration
FMQL45T900
EMIO5
Device Tree Entry:
<&portc 5 0x01>;
*/
#define DEV_NAME "gpio"
#define GPIO_NUM 431 // Corresponds to EMIO5
A typical Makefile for building the kernel module might look like this (ensure KERN_DIR points to your kernel source):
ifneq ($(KERNELRELEASE),)
obj-m := gpio_irq.o
else
KERN_DIR = /home/FMQL-Linux-SDK-Prj-20220223/linux-4.14.55-fmsh
all:
make -C $(KERN_DIR) M=$(shell pwd) modules
clean:
make -C $(KERN_DIR) M=$(shell pwd) modules clean
rm -rf modules.order
endif
Key GPIO and interrupt handling functions used in the driver include:
| Function | Parameters | Description |
|---|---|---|
gpio_is_valid |
GPIO number | Validates GPIO availability |
gpio_request |
GPIO number | Requests control of GPIO |
gpio_direction_input |
GPIO number | Sets GPIO as input |
gpio_get_value |
GPIO number | Reads current value |
gpio_direction_output |
GPIO number, value | Configures GPIO as output |
gpio_set_value |
GPIO number, value | Writes specified level |
gpio_to_irq |
GPIO number | Maps GPIO to IRQ line |
request_irq |
IRQ number, handler, flags | Registers interrupt handler |
free_irq |
IRQ number | Releases registered IRQ |
gpio_free |
GPIO number | Releases GPIO resource |
Sample initialization sequence within the driver:
if (!gpio_is_valid(gpio_num)) {
printk(KERN_ERR "Invalid GPIO number\n");
return -EINVAL;
}
printk(KERN_INFO "Using GPIO%d\n", gpio_num);
ret = gpio_request(gpio_num, "interrupt_gpio");
if (ret < 0) {
printk(KERN_ERR "Cannot request GPIO%d\n", gpio_num);
return ret;
}
ret = gpio_direction_input(gpio_num);
if (ret < 0) {
printk(KERN_ERR "Failed to set direction for GPIO%d\n", gpio_num);
goto fail_dir;
}
int initial_level = gpio_get_value(gpio_num);
printk(KERN_INFO DEV_NAME " Initial state: %d\n", initial_level);
unsigned int irq_line = gpio_to_irq(gpio_num);
printk(KERN_INFO "Mapped to IRQ line %d\n", irq_line);
ret = request_irq(irq_line,
gpio_interrupt_handler,
IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
"gpio_interrupt",
NULL);
if (ret < 0) {
printk(KERN_ERR "Unable to register IRQ handler\n");
goto fail_irq;
}
...
return 0;
fail_irq:
gpio_free(gpio_num);
fail_dir:
return ret;
After compiling the module using make, load it into the kernel with:
insmod gpio_irq.ko