Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mapping device tree interrupt flags to devm_request_irq

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?

like image 336
Damien Avatar asked Oct 13 '16 03:10

Damien


1 Answers

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).

like image 54
Sam Protsenko Avatar answered Nov 09 '22 23:11

Sam Protsenko