Reputation: 1046
I have a GPIO peripheral, defined in Device Tree as that:
gpio0: gpio@2300000
{
compatible = "fsl,qoriq-gpio";
reg = <0x0 0x2300000 0x0 0x10000>;
interrupts = <GIC_SPI 66 IRQ_TYPE_LEVEL_HIGH>;
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
#interrupt-cells = <2>;
};
I want to write an interrupt handler for this (as a kernel module). But this IRQ number (66) is a hardware one and I need a virtual, Linux IRQ number to pass it to request_irq
.
How can I get this number? There is only one interrupt controller (GIC).
Is there a way to do this without writing a platform device driver (as there is probably already one working in the system and I think I cannot register another one).
Upvotes: 5
Views: 6784
Reputation: 5989
I have once done that (finding linux irq number corresponding to my hardware irq number).
In linux there is a radix tree of irq_desc which is indexed by linux irq number(virtual). irq_desc contains irq_data.hw_irq so if you index through the irq_desc radix tree (using irq_to_desc function) and search for the one that has the matching hw_irq number, then you have found it(the index is the linux irq number).
I copy the code here (ref : http://lists.kernelnewbies.org/pipermail/kernelnewbies/2022-April/022526.html )
# include <linux/interrupt.h>
# include <linux/irq.h>
# include <linux/irqdesc.h>
// find my irq number
for(i=0;i<NR_IRQS;i++){ // i is the linux irq number
desc = irq_to_desc(i);
if (desc) {
//printk("irq_desc(%d)->irq_data.hwirq = %ld\n", i,
desc->irq_data.hwirq);
if (desc->irq_data.hwirq == 47) break;
// 47 is the hwirq number, (SPI 15, 32(16 SGI, 16 PPI) +15=47)
}
}
if (i == NR_IRQS) {
printk("couldn't find irq number..\n");
goto r_device;
}
linux_irq = i;
ret = request_irq(linux_irq, axpu_irq_handler, IRQF_SHARED, "axpu_irq",
&axpu_cdev);
Upvotes: 1
Reputation: 531
For whom is not trying to create a GPIO driver but still need to get Linux virtual IRQ from HW IRQ, there is a specific API for platform drivers. You can register a platform driver and then, during the probing, call
/**
* platform_get_irq - get an IRQ for a device
* @dev: platform device
* @num: IRQ number index
*
* Gets an IRQ for a platform device and prints an error message if finding the
* IRQ fails. Device drivers should check the return value for errors so as to
* not pass a negative integer value to the request_irq() APIs.
*
* Return: non-zero IRQ number on success, negative error number on failure.
*/
int platform_get_irq(struct platform_device *dev, unsigned int num);
To reach your goal, you have a lot of different options:
of_irq_get
)platform_get_irq
)If your platform has a programmable GPIO, you can use the GPIO Driver Interface. See @yashC reply. In your specific case, given that your device is part of GPIO, you should go for this approach.
If you want to interact directly with the device tree, you can use this solution. Imho, you should follow this approach only if you are writing a specific (and non-generic) driver and you need a "dirty and clean" way to go.
static const struct of_device_id qoriq_gpio_match_table[] =
{
{ .compatible = "fsl,qoriq-gpio" },
{ }
};
np = of_find_matching_node(NULL, qoriq_gpio_match_table);
if (!np)
{
pr_err("No device tree node for qoriq-gpio\n");
return -ENODEV;
}
// decode a node's IRQ and return it as a Linux IRQ number
irq_num = of_irq_get(np, 0);
// request_irq(...)
Basically, you have to register a platform driver
.
static const struct of_device_id qoriq_gpio_match_table[] =
{
{ .compatible = "fsl,qoriq-gpio" },
{ }
};
static struct platform_driver qoriq_gpi_driver = {
.driver = {
.name = "qoriq-gpio",
.of_match_table = qoriq_gpio_match_table
},
.probe = qoriq_gpio_probe
};
static int qoriq_gpio_probe(struct platform_device *pdev)
{
irq = platform_get_irq(pdev, 0);
if (irq < 0)
return irq;
// request_irq(...)
}
Until Kernel v5.19 you were able to use also: platform_get_resource(pdev, IORESOURCE_IRQ, 0);
API that, currently, is no more available.
You should use this approach if your device is a bit more generic (eg. you are working with several boards with different DTs).
Upvotes: 1
Reputation: 1005
As per you comment you want to register a GPIO as an interrupt. The node of device tree that you have posted is the interrupt controller node, which wont concern us for the task we have at hand.
To register a gpio as an interrupt, you first need to find a GPIO which can be configured as an interrupt (in most modern processors all GPIOs support it) and then you have to make sure it is not used by some other device by multiplexing it (if it is used by some one like SPI or UART etc , you can disable them from device tree if you are not using that entity).
now that you have a GPIO pin that you can use. Find the GPIO number on kernel that pin corresponds to (it depends on the architecture of your processor and its carrier board).
When you have that you can just write a simple module that will export your GPIO and use it as interrupt.
Below is a snippet from http://derekmolloy.ie
gpio_request(gpioButton, "sysfs"); // Set up the gpioButton
gpio_direction_input(gpioButton); // Set the button GPIO to be an input
gpio_set_debounce(gpioButton, 200); // Debounce the button with a delay of 200ms
gpio_export(gpioButton, false); // Causes gpio115 to appear in /sys/class/gpio
// the bool argument prevents the direction from being changed
// Perform a quick test to see that the button is working as expected on LKM load
printk(KERN_INFO "GPIO_TEST: The button state is currently: %d\n", gpio_get_value(gpioButton));
// GPIO numbers and IRQ numbers are not the same! This function performs the mapping for us
irqNumber = gpio_to_irq(gpioButton);
printk(KERN_INFO "GPIO_TEST: The button is mapped to IRQ: %d\n", irqNumber);
// This next call requests an interrupt line
result = request_irq(irqNumber, // The interrupt number requested
(irq_handler_t) ebbgpio_irq_handler, // The pointer to the handler function below
IRQF_TRIGGER_RISING, // Interrupt on rising edge (button press, not release)
"ebb_gpio_handler", // Used in /proc/interrupts to identify the owner
NULL); // The *dev_id for shared interrupt lines, NULL is okay
Link to the complete code.
Upvotes: 3