8

I am currently writing a device driver for Linux for use of PowerPC.

The device tree entry is as follows:

// PPS Interrupt client 
pps_hwirq {
    compatible = "pps-hwirq";
    interrupts = <17 0x02>;                     // IPIC 17 = IRQ1, 0x02 = falling edge
    interrupt-parent = < &ipic >;
};

The 0x02 flag is quite important - the PPS is aligned with the falling edge, but this is not universal on GPS receivers and therefore should be configurable.

In the probe() function of the driver, obtaining the IRQ number is straightforward:

hwirq = irq_of_parse_and_map(np, 0);
if (hwirq == NO_IRQ) {
    dev_err(&pdev->dev, "No interrupt found in the device tree\n");
    return -EINVAL;
}

But how does one map the the IRQ flags from the device tree to the driver?

/* ****TODO****: Get the interrupt flags from the device tree 
 * For now, hard code to suit my problem, but since this differs
 * by GPS receiver, it should be configurable.
 */
flags = IRQF_TRIGGER_FALLING;

/* register IRQ interrupt handler */
ret = devm_request_irq(&pdev->dev, data->irq, pps_hwint_irq_handler,
                       flags, data->info.name, data);

Unfortunately, there are few - if any - examples in the tree that actually do this job - most leave this flag as 0 (leave as-is) - here's a snippet of the results when grep for devm_request_irq, noting the values for the flags:

./drivers/crypto/mxs-dcp.c:     ret = devm_request_irq(dev, dcp_vmi_irq, mxs_dcp_irq, 0,
./drivers/crypto/mxs-dcp.c:     ret = devm_request_irq(dev, dcp_irq, mxs_dcp_irq, 0,
./drivers/crypto/omap-sham.c:   err = devm_request_irq(dev, dd->irq, dd->pdata->intr_hdlr,
./drivers/crypto/omap-aes.c:            err = devm_request_irq(dev, irq, omap_aes_irq, 0,
./drivers/crypto/picoxcell_crypto.c:    if (devm_request_irq(&pdev->dev, irq->start, spacc_spacc_irq, 0,

Or hard code it to what the hardware actually asserts:

./drivers/crypto/tegra-aes.c:   err = devm_request_irq(dev, dd->irq, aes_irq, IRQF_TRIGGER_HIGH |

So how does one cleanly associate this property from the device tree to the actual driver?

Sam Protsenko
  • 14,045
  • 4
  • 59
  • 75
Damien
  • 785
  • 3
  • 8
  • 18
  • What is the type of driver you're trying to write? In other words, what is the function you are using to register your driver? – Sam Protsenko Oct 13 '16 at 12:52
  • The actual type of driver is irrelevant to the question - I have it working with the flags hard-coded to IRQF_TRIGGER_FALLING, which is what essentially very other driver does (or sets it to zero for no change). What I want to do is full this value from the device tree and give it devm_request_irq. – Damien Oct 14 '16 at 00:54

1 Answers1

10

Further I'm gonna show how to obtain IRQ number and IRQ flags from Device Tree in some common cases:

  • in I2C drivers
  • in platform drivers
  • manually

In I2C drivers

In short

If you're writing an I2C driver, you don't need to read IRQ parameters from DT manually. You can rely on I2C core to populate IRQ parameters for you:

  • in your probe() function, client->irq will contain the IRQ number
  • devm_request_irq() will use IRQ flags from DT automatically (just don't pass any IRQ trigger flags to that function).

Details

Let's look at the i2c_device_probe() function (it's where your driver's probe() function is being called from):

static int i2c_device_probe(struct device *dev)
{
    ...
    if (dev->of_node) {
        ...
        irq = of_irq_get(dev->of_node, 0);
    }
    ...
    client->irq = irq;
    ...
    status = driver->probe(client, i2c_match_id(driver->id_table, client));
}

So, client->irq will already contain IRQ number in your driver's probe function.

As for IRQ flags: of_irq_get() (in code above) eventually calls irqd_set_trigger_type(), which internally stores IRQ flags (read from device tree) for your interrupt number. So, when you call devm_request_irq(), it eventually ends up in __setup_irq(), and it does next:

/*
 * If the trigger type is not specified by the caller,
 * then use the default for this interrupt.
 */
if (!(new->flags & IRQF_TRIGGER_MASK))
    new->flags |= irqd_get_trigger_type(&desc->irq_data);

where:

  • new->flags contains flags you provided to devm_request_irq()
  • irqd_get_trigger_type() returns flags obtained from DT

In other words, if you don't pass IRQ flags to devm_request_irq() (e.g. pass 0), it will use IRQ flags obtained from device tree.

See also this question for details.

In platform drivers

You can use platform_get_irq() to obtain IRQ number. It also stores (internally) IRQ flags obtained from DT, so if you pass flags=0 to devm_request_irq(), flags from DT will be used.

Manually

If your driver doesn't rely on kernel frameworks, you have to obtain IRQ values manually:

  • IRQ number can be obtained (as you mentioned) by irq_of_parse_and_map(); this function not only returns IRQ number, but also stores IRQ flags for your IRQ number (by calling irqd_set_trigger_type() eventually); stored IRQ flags will be automatically used in devm_request_irq(), if you don't pass IRQ trigger type to it (e.g. you can pass flags=0)

  • IRQ flags can be obtained by irq_get_trigger_type(), but only after executing irq_of_parse_and_map()

    So probably you only need to run irq_of_parse_and_map() and let devm_request_irq() handle flags for you (just make sure you don't pass trigger flags to it).

Community
  • 1
  • 1
Sam Protsenko
  • 14,045
  • 4
  • 59
  • 75
  • do you know why if I use request_irq() or devm_request_irq() for an SPI device (for which spi_drv_probe does almost the same as i2c_device_probe does), then after unloading the module and of course doing free_irq or even explicitly calling devm_free_irq, and then trying to load the same module again, I get an "irq: type mismatch, failed to map hwirq..." message? – Alexander Amelkin Mar 29 '17 at 18:18
  • @AlexanderAmelkin My guess is some other device clashes with that IRQ (maybe some bug in some driver), or your `free_irq()` doesn't get called (or doesn't finish successfully). Perhaps some debugging is needed to figure out the root cause. Can you apply patch from [here](https://www.spinics.net/lists/arm-kernel/msg528469.html) and paste the corresponding output from kernel log? Btw, I guess it would be better to create a new question on SO, as it seems to be out of scope here. – Sam Protsenko Mar 29 '17 at 18:46
  • thanks for the link to the patch. I definitely will do debugging and probably will post a new question/answer myself. I just thought that this could have been some known issue that I'm unaware of or something that I could have been doing wrong. Thanks. – Alexander Amelkin Mar 30 '17 at 09:50